Compare commits

...

2 Commits

Author SHA1 Message Date
ea5093f2c2 Handling mongo indexes in firm 2025-05-04 02:26:06 +02:00
b542fd40a6 Renaming Collection CurrentFirmModel to CurrentFirm 2025-05-04 00:26:22 +02:00
11 changed files with 75 additions and 65 deletions

View File

@@ -7,7 +7,8 @@ from beanie import PydanticObjectId
from pydantic import BaseModel, Field, ConfigDict from pydantic import BaseModel, Field, ConfigDict
from pydantic.json_schema import SkipJsonSchema from pydantic.json_schema import SkipJsonSchema
from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry, ForeignKey from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry, ForeignKey, \
CrudDocumentConfig
from firm.core.filter import Filter, FilterSchema from firm.core.filter import Filter, FilterSchema
from firm.entity.models import Entity from firm.entity.models import Entity
@@ -98,15 +99,6 @@ class ContractDraft(CrudDocument):
status: ContractDraftStatus = Field(default=ContractDraftStatus.in_progress, title="Statut") status: ContractDraftStatus = Field(default=ContractDraftStatus.in_progress, title="Statut")
todo: List[str] = Field(default=[], title="Reste à faire") todo: List[str] = Field(default=[], title="Reste à faire")
class Settings(CrudDocument.Settings):
fulltext_search = ['name', 'title']
bson_encoders = {
datetime.date: lambda dt: dt if hasattr(dt, 'hour')
else datetime.datetime(year=dt.year, month=dt.month, day=dt.day,
hour=0, minute=0, second=0)
}
async def check_is_ready(self, db): async def check_is_ready(self, db):
if self.status == ContractDraftStatus.published: if self.status == ContractDraftStatus.published:
return return
@@ -150,6 +142,11 @@ class Contract(CrudDocument):
Contrat publié. Les contrats ne peuvent pas être modifiés. Contrat publié. Les contrats ne peuvent pas être modifiés.
Ils peuvent seulement être signés par les parties et imprimés par l'avocat Ils peuvent seulement être signés par les parties et imprimés par l'avocat
""" """
model_config = ConfigDict(title='Contrat')
document_config = CrudDocumentConfig(
indexes=["parties.signature_uuid"],
)
name: str = Field(title="Nom") name: str = Field(title="Nom")
title: str = Field(title="Titre") title: str = Field(title="Titre")
parties: List[Party] = Field(title="Parties") parties: List[Party] = Field(title="Parties")
@@ -170,15 +167,6 @@ class Contract(CrudDocument):
contract_label = f"{contract_label} - {self.date.strftime('%m/%d/%Y')}" contract_label = f"{contract_label} - {self.date.strftime('%m/%d/%Y')}"
return contract_label return contract_label
class Settings(CrudDocument.Settings):
fulltext_search = ['name', 'title']
bson_encoders = {
datetime.date: lambda dt: dt if hasattr(dt, 'hour')
else datetime.datetime(year=dt.year, month=dt.month, day=dt.day,
hour=0, minute=0, second=0)
}
@classmethod @classmethod
async def find_by_signature_id(cls, db, signature_id: UUID): async def find_by_signature_id(cls, db, signature_id: UUID):
request = {'parties': {"$elemMatch": {"signature_uuid": str(signature_id) }}} request = {'parties': {"$elemMatch": {"signature_uuid": str(signature_id) }}}

View File

@@ -2,7 +2,7 @@ from fastapi import HTTPException, Depends
from hub.auth import get_current_user from hub.auth import get_current_user
from firm.current_firm import CurrentFirmModel, Partner from firm.current_firm import CurrentFirm, Partner
from firm.db import get_db_client from firm.db import get_db_client
from firm.entity.models import Entity from firm.entity.models import Entity
@@ -16,7 +16,7 @@ class Registry:
self.instance = instance self.instance = instance
self.firm = firm self.firm = firm
self.current_firm = CurrentFirmModel.get_current(self.db) self.current_firm = CurrentFirm.get_current(self.db)
async def set_user(self, user): async def set_user(self, user):
for firm in user.firms: for firm in user.firms:

View File

@@ -1,12 +1,18 @@
from datetime import datetime, UTC from datetime import datetime, UTC
from typing import Optional from typing import Optional, TypedDict, ClassVar
from beanie import PydanticObjectId from beanie import PydanticObjectId
from motor.motor_asyncio import AsyncIOMotorCollection from motor.motor_asyncio import AsyncIOMotorCollection
from pydantic import BaseModel, Field, computed_field from pydantic import BaseModel, Field, computed_field
class CrudDocumentConfig(TypedDict, total=False):
fulltext_search: list[str]
indexes: list[str]
class CrudDocument(BaseModel): class CrudDocument(BaseModel):
document_config: ClassVar[CrudDocumentConfig] = CrudDocumentConfig()
id: Optional[PydanticObjectId] = Field(default=None) id: Optional[PydanticObjectId] = Field(default=None)
created_at: datetime = Field(default=datetime.now(UTC), nullable=False, title="Créé le") created_at: datetime = Field(default=datetime.now(UTC), nullable=False, title="Créé le")
created_by: Optional[PydanticObjectId] = Field(default=None, title="Créé par") created_by: Optional[PydanticObjectId] = Field(default=None, title="Créé par")
@@ -17,16 +23,13 @@ class CrudDocument(BaseModel):
def _id(self): def _id(self):
return self.id return self.id
@computed_field @computed_field(title="Label")
def label(self) -> str: def label(self) -> str:
return self.compute_label() return self.compute_label()
def compute_label(self) -> str: def compute_label(self) -> str:
return "" return ""
class Settings:
fulltext_search = []
@classmethod @classmethod
def _collection_name(cls): def _collection_name(cls):
return cls.__name__ return cls.__name__
@@ -35,6 +38,10 @@ class CrudDocument(BaseModel):
def _get_collection(cls, db) -> AsyncIOMotorCollection: def _get_collection(cls, db) -> AsyncIOMotorCollection:
return db.get_collection(cls._collection_name()) return db.get_collection(cls._collection_name())
@classmethod
def create_index(cls, db, index):
cls._get_collection(db).create_index(index)
@classmethod @classmethod
async def create(cls, db, create_schema): async def create(cls, db, create_schema):
model_dict = create_schema.model_dump() | {"created_by": db.partner.id, "updated_by": db.partner.id} model_dict = create_schema.model_dump() | {"created_by": db.partner.id, "updated_by": db.partner.id}
@@ -84,7 +91,6 @@ class CrudDocument(BaseModel):
await cls._get_collection(db).delete_one({"_id": model.id}) await cls._get_collection(db).delete_one({"_id": model.id})
def text_area(*args, **kwargs): def text_area(*args, **kwargs):
kwargs['widget'] = { kwargs['widget'] = {
"formlyConfig": { "formlyConfig": {

View File

@@ -3,12 +3,11 @@ from typing import Any
from beanie import PydanticObjectId from beanie import PydanticObjectId
from pydantic import Field from pydantic import Field
from firm.core.models import CrudDocument from firm.core.models import CrudDocument, CrudDocumentConfig
from firm.core.schemas import Writer, Reader from firm.core.schemas import Writer, Reader
from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate, EntityRead from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate, EntityRead
class CurrentFirm(CrudDocument):
class CurrentFirmModel(CrudDocument):
instance: str = Field() instance: str = Field()
firm: str = Field() firm: str = Field()
entity_id: PydanticObjectId = Field() entity_id: PydanticObjectId = Field()
@@ -60,6 +59,10 @@ class CurrentFirmSchemaUpdate(Writer):
pass pass
class Partner(CrudDocument): class Partner(CrudDocument):
document_config = CrudDocumentConfig(
indexes=["user_id", "entity_id"],
)
user_id: PydanticObjectId = Field() user_id: PydanticObjectId = Field()
entity_id: PydanticObjectId = Field() entity_id: PydanticObjectId = Field()

View File

@@ -1,7 +1,7 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from firm.core.depends import get_authed_tenant_registry, get_uninitialized_registry from firm.core.depends import get_authed_tenant_registry, get_uninitialized_registry
from firm.current_firm import CurrentFirmModel, CurrentFirmSchemaRead, CurrentFirmSchemaCreate, CurrentFirmSchemaUpdate, Partner from firm.current_firm import CurrentFirm, CurrentFirmSchemaRead, CurrentFirmSchemaCreate, CurrentFirmSchemaUpdate, Partner
from firm.entity.models import Entity, Employee from firm.entity.models import Entity, Employee
from firm.entity.schemas import EntityRead from firm.entity.schemas import EntityRead
@@ -9,7 +9,7 @@ current_firm_router = APIRouter()
@current_firm_router.get("/", response_model=CurrentFirmSchemaRead, response_description=f"Current Firm records retrieved") @current_firm_router.get("/", response_model=CurrentFirmSchemaRead, response_description=f"Current Firm records retrieved")
async def read(reg=Depends(get_authed_tenant_registry)) -> CurrentFirmSchemaRead: async def read(reg=Depends(get_authed_tenant_registry)) -> CurrentFirmSchemaRead:
document = await CurrentFirmModel.get_current(reg.db) document = await CurrentFirm.get_current(reg.db)
firm_entity = await Entity.get(reg.db, document.entity_id) firm_entity = await Entity.get(reg.db, document.entity_id)
partner = await Partner.get_by_user_id(reg.db, reg.user.id) partner = await Partner.get_by_user_id(reg.db, reg.user.id)
partner = await Entity.get(reg.db, partner.entity_id) partner = await Entity.get(reg.db, partner.entity_id)
@@ -31,7 +31,7 @@ async def create(schema: CurrentFirmSchemaCreate, reg=Depends(get_uninitialized_
corporation_schema.entity_data.employees.append(Employee(entity_id=owner_entity.id, position=schema.position)) corporation_schema.entity_data.employees.append(Employee(entity_id=owner_entity.id, position=schema.position))
corp = await Entity.create(reg.db, corporation_schema) corp = await Entity.create(reg.db, corporation_schema)
document = await CurrentFirmModel.create(reg.db, CurrentFirmModel( document = await CurrentFirm.create(reg.db, CurrentFirm(
instance=reg.instance, instance=reg.instance,
firm=reg.firm, firm=reg.firm,
entity_id=corp.id, entity_id=corp.id,
@@ -42,6 +42,6 @@ async def create(schema: CurrentFirmSchemaCreate, reg=Depends(get_uninitialized_
@current_firm_router.put("/", response_description=f"Current Firm record updated") @current_firm_router.put("/", response_description=f"Current Firm record updated")
async def update(schema: CurrentFirmSchemaUpdate, reg=Depends(get_authed_tenant_registry)) -> CurrentFirmSchemaRead: async def update(schema: CurrentFirmSchemaUpdate, reg=Depends(get_authed_tenant_registry)) -> CurrentFirmSchemaRead:
document = await CurrentFirmModel.get_current(reg.db) document = await CurrentFirm.get_current(reg.db)
document = await CurrentFirmModel.update(reg.db, document, schema) document = await CurrentFirm.update(reg.db, document, schema)
return CurrentFirmSchemaRead.from_model(document) return CurrentFirmSchemaRead.from_model(document)

View File

@@ -11,9 +11,6 @@ client = motor.motor_asyncio.AsyncIOMotorClient(
DATABASE_URL, uuidRepresentation="standard" DATABASE_URL, uuidRepresentation="standard"
) )
async def init_db():
pass
async def stop_db(): async def stop_db():
client.close() client.close()

View File

@@ -1,8 +1,8 @@
from datetime import date, datetime from datetime import date
from typing import List, Literal, Optional from typing import List, Literal, Optional
from pydantic import Field, BaseModel, ConfigDict from pydantic import Field, BaseModel, ConfigDict
from beanie import Indexed, PydanticObjectId from beanie import PydanticObjectId
from firm.core.models import CrudDocument, ForeignKey from firm.core.models import CrudDocument, ForeignKey
from firm.core.filter import Filter, FilterSchema from firm.core.filter import Filter, FilterSchema
@@ -18,10 +18,10 @@ class Individual(EntityType):
model_config = ConfigDict(title='Particulier') model_config = ConfigDict(title='Particulier')
type: Literal['individual'] = 'individual' type: Literal['individual'] = 'individual'
firstname: Indexed(str) = Field(title='Prénom') firstname: str = Field(title='Prénom')
middlename: Indexed(str) = Field(default="", title='Autres prénoms') middlename: str = Field(default="", title='Autres prénoms')
lastname: Indexed(str) = Field(title='Nom de famille') lastname: str = Field(title='Nom de famille')
surnames: List[Indexed(str)] = Field( surnames: List[str] = Field(
default=[], default=[],
props={"items_per_row": "4", "numbered": True}, props={"items_per_row": "4", "numbered": True},
title="Surnoms" title="Surnoms"
@@ -32,14 +32,14 @@ class Individual(EntityType):
@property @property
def label(self) -> str: def label(self) -> str:
# if len(self.surnames) > 0: # if len(self.surnames) > 0:
# return '{} "{}" {}'.format(self.firstname, self.surnames[0], self.lastname) # return f"{self.firstname} \"{self.surnames[0]}\" {self.lastname}"
return f"{self.firstname} {self.lastname}" return f"{self.firstname} {self.lastname}"
class Employee(BaseModel): class Employee(BaseModel):
model_config = ConfigDict(title='Fiche Employé') model_config = ConfigDict(title='Fiche Employé')
position: Indexed(str) = Field(title='Poste') position: str = Field(title='Poste')
entity_id: PydanticObjectId = ForeignKey("entities", "Entity", title='Employé') entity_id: PydanticObjectId = ForeignKey("entities", "Entity", title='Employé')
@@ -47,8 +47,8 @@ class Corporation(EntityType):
model_config = ConfigDict(title='Entreprise') model_config = ConfigDict(title='Entreprise')
type: Literal['corporation'] = 'corporation' type: Literal['corporation'] = 'corporation'
title: Indexed(str) = Field(title='Dénomination sociale') title: str = Field(title='Dénomination sociale')
activity: Indexed(str) = Field(title='Activité') activity: str = Field(title='Activité')
employees: List[Employee] = Field(default=[], title='Employés') employees: List[Employee] = Field(default=[], title='Employés')
@@ -72,15 +72,6 @@ class Entity(CrudDocument):
return "" return ""
return self.entity_data.label return self.entity_data.label
class Settings(CrudDocument.Settings):
fulltext_search = ['label']
bson_encoders = {
date: lambda dt: dt if hasattr(dt, 'hour')
else datetime(year=dt.year, month=dt.month, day=dt.day,
hour=0, minute=0, second=0)
}
class EntityFilters(FilterSchema): class EntityFilters(FilterSchema):
class Constants(Filter.Constants): class Constants(Filter.Constants):

View File

@@ -0,0 +1,31 @@
import logging
from firm.contract.models import Contract, ContractDraft
from firm.core.depends import Registry
from firm.current_firm import CurrentFirm, Partner
from firm.db import client
from firm.entity.models import Entity
from firm.template.models import ContractTemplate, ProvisionTemplate
from hub.firm import Firm
collections = [CurrentFirm, Entity, Partner, Contract, ContractDraft, ContractTemplate, ProvisionTemplate]
logger = logging.getLogger('uvicorn.error')
async def create_documents_indexes(db):
for collection in collections:
if "indexes" in collection.document_config:
for field in collection.document_config["indexes"]:
collection.create_index(db, field)
async def init_all_db():
logger.info("[FRM] Creating index for firms")
for firm in await Firm.find({}).to_list():
reg = Registry(client, firm.instance, firm.firm)
await reg.current_firm
await create_documents_indexes(reg.db)
async def init_db():
await init_all_db()

View File

@@ -36,9 +36,6 @@ class ProvisionTemplate(CrudDocument):
def compute_label(self) -> str: def compute_label(self) -> str:
return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\"" return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\""
class Settings(CrudDocument.Settings):
fulltext_search = ['name', 'title', 'body']
class ProvisionTemplateReference(BaseModel): class ProvisionTemplateReference(BaseModel):
model_config = ConfigDict(title="Clause") model_config = ConfigDict(title="Clause")
@@ -67,9 +64,6 @@ class ContractTemplate(CrudDocument):
def compute_label(self) -> str: def compute_label(self) -> str:
return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\"" return f"{self.name} - \"{unescape(remove_html_tags(self.title))}\""
class Settings(CrudDocument.Settings):
fulltext_search = ['name', 'title']
class ContractTemplateFilters(FilterSchema): class ContractTemplateFilters(FilterSchema):
class Constants(Filter.Constants): class Constants(Filter.Constants):

View File

@@ -5,7 +5,7 @@ from hub import hub_router
from hub.db import init_db as hub_init_db from hub.db import init_db as hub_init_db
from firm import firm_router from firm import firm_router
from firm.db import init_db as firm_init_db from firm.init_db import init_db as firm_init_db
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -189,7 +189,7 @@
}, },
"corporation": { "corporation": {
"type": "Entreprise", "type": "Entreprise",
"title": "Titre", "title": "Dénomination sociale",
"activity": "Activité", "activity": "Activité",
"employees": "Employés", "employees": "Employés",
"resource_title": "Entreprise" "resource_title": "Entreprise"