import datetime from typing import List, Literal from enum import Enum from pydantic import BaseModel, Field from beanie.operators import ElemMatch from ..core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry from ..entity.models import Entity class ContractStatus(str, Enum): published = 'published' signed = 'signed' printed = 'printed' executed = 'executed' class ContractDraftStatus(str, Enum): in_progress = 'in_progress' ready = 'ready' created = 'created' class DraftParty(BaseModel): entity_id: str = Field( foreignKey={ "reference": { "resource": "entity", "schema": "Entity", } } ) part: str representative_id: str = Field( foreignKey={ "reference": { "resource": "entity", "schema": "Entity", } }, default="" ) class Signature(BaseModel): uuid: str affixed: bool class Party(BaseModel): entity: Entity part: str representative: Entity = None signature_uuid: str signature_affixed: bool = False signature_png: str = None class ProvisionGenuine(BaseModel): type: Literal['genuine'] = 'genuine' title: str = RichtextSingleline(props={"parametrized": True}) body: str = RichtextMultiline(props={"parametrized": True}) class ContractProvisionTemplateReference(BaseModel): type: Literal['template'] = 'template' provision_template_id: str = Field( foreignKey={ "reference": { "resource": "template/provision", "schema": "ProvisionTemplate", "displayedFields": ['title', 'body'] }, }, props={"parametrized": True} ) class DraftProvision(BaseModel): provision: ContractProvisionTemplateReference | ProvisionGenuine = Field(..., discriminator='type') class Provision(BaseModel): title: str = RichtextSingleline() body: str = RichtextMultiline() class ContractDraft(CrudDocument): name: str title: str parties: List[DraftParty] provisions: List[DraftProvision] = Field( props={"items-per-row": "1", "numbered": True} ) variables: List[DictionaryEntry] = Field( default=[], format="dictionary", ) status: ContractDraftStatus = ContractDraftStatus.in_progress class Settings(CrudDocument.Settings): 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) } class Contract(CrudDocument): name: str title: str parties: List[Party] provisions: List[Provision] = Field( props={"items-per-row": "1", "numbered": True} ) status: ContractStatus = ContractStatus.published lawyer: Entity location: str date: datetime.date class Settings(CrudDocument.Settings): # fulltext_search = ['label'] 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 def find_by_signature_id(cls, signature_id: str): crit = ElemMatch(cls.parties, {"signature_uuid": signature_id}) return cls.find_one(crit) def get_signature(self, signature_id: str): for p in self.parties: if p.signature_uuid == signature_id: return p def get_signature_index(self, signature_id: str): for i, p in enumerate(self.parties): if p.signature_uuid == signature_id: return i def is_signed(self): for p in self.parties: if not p.signature_affixed: return False return True def replace_variables_in_value(variables, value: str): for v in variables: if v.value: value = value.replace('%{}%'.format(v.key), v.value) return value