Adding multi tenant check and Starting firm initialization

This commit is contained in:
2025-04-12 18:12:56 +02:00
parent 9ef599bbd5
commit c7e946f963
14 changed files with 205 additions and 39 deletions

View File

@@ -3,10 +3,12 @@ from fastapi import APIRouter
from firm.entity import entity_router
from firm.template import template_router
from firm.contract import contract_router
from firm.current_firm.routes import current_firm_router
firm_router = APIRouter(prefix="/{instance}/{firm}")
firm_router.include_router(current_firm_router, tags=["Current Firm"])
firm_router.include_router(entity_router, prefix="/entities", tags=["Entity"], )
firm_router.include_router(template_router, prefix="/templates", tags=["Template"], )
firm_router.include_router(contract_router, prefix="/contracts", )

View File

@@ -12,7 +12,7 @@ from weasyprint.text.fonts import FontConfiguration
from pathlib import Path
from firm.core.routes import get_tenant_db_cursor
from firm.core.depends import get_tenant_db_cursor
from firm.entity.models import Entity
from firm.template.models import ProvisionTemplate
from firm.contract.models import ContractDraft, Contract, ContractStatus, replace_variables_in_value

View File

@@ -1,7 +1,8 @@
import uuid
from fastapi import Depends, HTTPException
from firm.core.routes import get_crud_router, get_logged_tenant_db_cursor
from firm.core.routes import get_crud_router
from firm.core.depends import get_logged_tenant_db_cursor
from firm.contract.models import Contract, ContractDraft, ContractDraftStatus, replace_variables_in_value, ContractFilters
from firm.contract.schemas import ContractCreate, ContractRead, ContractUpdate, ContractInit

View File

@@ -1,7 +1,8 @@
from beanie import PydanticObjectId
from fastapi import HTTPException, Depends
from firm.core.routes import get_crud_router, get_logged_tenant_db_cursor
from firm.core.routes import get_crud_router
from firm.core.depends import get_logged_tenant_db_cursor
from firm.contract.models import ContractDraft, ContractDraftStatus, ContractDraftFilters
from firm.contract.schemas import ContractDraftCreate, ContractDraftRead, ContractDraftUpdate

View File

@@ -4,7 +4,7 @@ import shutil
from uuid import UUID
from firm.contract.models import Contract, Party
from firm.core.routes import get_tenant_db_cursor
from firm.core.depends import get_tenant_db_cursor
signature_router = APIRouter()

View File

@@ -0,0 +1,34 @@
from fastapi import HTTPException, Depends
from hub.auth import get_current_user
from firm.db import get_db_client
from firm.current_firm import CurrentFirmModel
async def get_tenant_db_cursor(instance: str, firm: str, db_client=Depends(get_db_client)):
db_cursor = db_client[f"tenant_{instance}_{firm}"]
current_firm = await CurrentFirmModel.get(db_cursor)
if current_firm is None:
raise HTTPException(status_code=405, detail=f"Firm needs to be instantiated first")
db_cursor.firm = current_firm
return db_cursor
def get_logged_tenant_db_cursor(db_cursor=Depends(get_tenant_db_cursor), user=Depends(get_current_user)):
for firm in user.firms:
if firm == db_cursor.firm:
db_cursor.user = user
return db_cursor
raise HTTPException(status_code=404, detail="This firm doesn't exist or you are not allowed to access it.")
async def get_uninitialized_tenant_db_cursor(instance: str, firm: str, db_client=Depends(get_db_client), user=Depends(get_current_user)):
db_cursor = db_client[f"tenant_{instance}_{firm}"]
current_firm = await CurrentFirmModel.get(db_cursor)
if current_firm is not None:
HTTPException(status_code=409, detail="Firm configuration already exists")
for firm in user.firms:
if firm == db_cursor.firm:
db_cursor.user = user
return db_cursor
raise HTTPException(status_code=404, detail="This firm doesn't exist or you are not allowed to access it.")

View File

@@ -9,9 +9,9 @@ from pydantic import BaseModel, Field, computed_field
class CrudDocument(BaseModel):
id: Optional[PydanticObjectId] = Field(default=None)
created_at: datetime = Field(default=datetime.now(UTC), nullable=False, title="Créé le")
# created_by: str
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False, title="Modifié le")
# updated_by: str
created_by: str = Field(nullable=False, title="Créé par")
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), nullable=False, title="Modifié le")
updated_by: str = Field(nullable=False, title="Modifié par")
@property
def _id(self):
@@ -37,8 +37,9 @@ class CrudDocument(BaseModel):
@classmethod
async def create(cls, db, create_schema):
values = cls.model_validate(create_schema.model_dump()).model_dump(mode="json")
result = await cls._get_collection(db).insert_one(values)
model_dict = create_schema.model_dump() | {"created_by": db.user, "updated_by":db.user}
document = cls.model_validate(model_dict).model_dump(mode="json")
result = await cls._get_collection(db).insert_one(document)
return await cls.get(db, result.inserted_id)
@@ -56,12 +57,12 @@ class CrudDocument(BaseModel):
@classmethod
async def get(cls, db, model_id):
value = await cls._get_collection(db).find_one({"_id": model_id})
if not value:
document = await cls._get_collection(db).find_one({"_id": model_id})
if not document:
return None
value["id"] = value.pop("_id")
return cls.model_validate(value)
document["id"] = document.pop("_id")
return cls.model_validate(document)
@classmethod
async def update(cls, db, model, update_schema):

View File

@@ -5,26 +5,9 @@ from fastapi_filter import FilterDepends
from fastapi_pagination import Page, add_pagination
from fastapi_pagination.ext.motor import paginate
from hub.auth import get_current_user
from firm.core.depends import get_logged_tenant_db_cursor
from firm.core.models import CrudDocument
from firm.core.schemas import Writer, Reader
from firm.db import get_db_client
#instance: str="westside", firm: str="cht",
def get_tenant_db_cursor(db_client=Depends(get_db_client)):
instance = "westside"
firm = "cht"
return db_client[f"tenant_{instance}_{firm}"]
#instance: str="westside", firm: str="cht",
def get_logged_tenant_db_cursor(db_client=Depends(get_db_client), user=Depends(get_current_user)):
instance = "westside"
firm = "cht"
db_cursor = db_client[f"tenant_{instance}_{firm}"]
db_cursor.user = user
return db_cursor
def get_crud_router(model: CrudDocument, model_create: Writer, model_read: Reader, model_update: Writer, model_filter):
model_name = model.__name__

View File

@@ -0,0 +1,45 @@
from typing import Any
from pydantic import Field
from firm.core.models import CrudDocument
from firm.core.schemas import Writer, Reader
from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate
class CurrentFirmModel(CrudDocument):
instance: str
firm: str
name: str = Field(nullable=False)
# primary_color: str = Field()
# secondary_color: str = Field()
def __eq__(self, other: Any) -> bool:
if isinstance(other, dict):
return self.instance == other["instance"] and self.firm == other["firm"]
return super().__eq__(other)
def compute_label(self) -> str:
return self.name
@classmethod
async def get(cls, db):
document = await cls._get_collection(db).find_one({})
if not document:
return None
document["id"] = document.pop("_id")
return cls.model_validate(document)
class CurrentFirmSchemaRead(Reader):
pass
class CurrentFirmSchemaCreate(Writer):
corporation: EntityCorporationCreate = Field(title="Informations sur la firme")
owner: EntityIndividualCreate = Field(title="Informations sur le dirigeant")
class CurrentFirmSchemaUpdate(Writer):
pass

View File

@@ -0,0 +1,22 @@
from fastapi import APIRouter, Depends
from firm.core.depends import get_logged_tenant_db_cursor, get_uninitialized_tenant_db_cursor
from firm.current_firm import CurrentFirmModel, CurrentFirmSchemaRead, CurrentFirmSchemaCreate, CurrentFirmSchemaUpdate
current_firm_router = APIRouter()
@current_firm_router.get("/", response_model=CurrentFirmSchemaRead, response_description=f"Current Firm records retrieved")
async def read(db=Depends(get_logged_tenant_db_cursor)) -> CurrentFirmSchemaRead:
return CurrentFirmSchemaRead.from_model(**CurrentFirmModel.get(db))
@current_firm_router.post("/", response_description=f"Current Firm added to the database")
async def create(schema: CurrentFirmSchemaCreate, db=Depends(get_uninitialized_tenant_db_cursor)) -> CurrentFirmSchemaRead:
await schema.validate_foreign_key(db)
record = await CurrentFirmModel.create(db, schema)
return CurrentFirmSchemaRead.from_model(record)
@current_firm_router.put("/", response_description=f"Current Firm record updated")
async def update(schema: CurrentFirmSchemaUpdate, db=Depends(get_logged_tenant_db_cursor)) -> CurrentFirmSchemaRead:
record = await CurrentFirmModel.get(db)
record = await CurrentFirmModel.update(db, record, schema)
return CurrentFirmSchemaRead.from_model(record)

View File

@@ -13,5 +13,11 @@ class EntityCreate(Writer):
class Config:
title = "Création d'un client"
class EntityIndividualCreate(EntityCreate):
entity_data: Individual
class EntityCorporationCreate(EntityCreate):
entity_data: Corporation
class EntityUpdate(EntityCreate):
pass