Compare commits

...

5 Commits

Author SHA1 Message Date
872e27e7e4 Initializable Firm 2025-04-16 22:39:09 +02:00
5bdb754f1c Switching foreign jkey to PydanticObjectId 2025-04-16 22:38:06 +02:00
15b77ff09f Changing role to position in comployee model 2025-04-16 21:32:49 +02:00
b0c4128e01 Shaping CurrentFirmSchemaCreate arch 2025-04-16 19:43:00 +02:00
e5a2539ec6 Adding UiSchema capacities to CrudForm 2025-04-16 19:42:28 +02:00
10 changed files with 87 additions and 47 deletions

View File

@@ -3,6 +3,7 @@ from typing import List, Literal, Optional
from enum import Enum from enum import Enum
from uuid import UUID from uuid import UUID
from beanie import PydanticObjectId
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry from firm.core.models import CrudDocument, RichtextSingleline, RichtextMultiline, DictionaryEntry
@@ -24,7 +25,7 @@ class ContractDraftStatus(str, Enum):
class DraftParty(BaseModel): class DraftParty(BaseModel):
entity_id: str = Field( entity_id: PydanticObjectId = Field(
foreignKey={ foreignKey={
"reference": { "reference": {
"resource": "entity", "resource": "entity",
@@ -35,7 +36,7 @@ class DraftParty(BaseModel):
title="Partie" title="Partie"
) )
part: str = Field(title="Rôle") part: str = Field(title="Rôle")
representative_id: str = Field( representative_id: PydanticObjectId = Field(
foreignKey={ foreignKey={
"reference": { "reference": {
"resource": "entity", "resource": "entity",
@@ -73,7 +74,7 @@ class ProvisionGenuine(BaseModel):
class ContractProvisionTemplateReference(BaseModel): class ContractProvisionTemplateReference(BaseModel):
type: Literal['template'] = ContractProvisionType.template type: Literal['template'] = ContractProvisionType.template
provision_template_id: str = Field( provision_template_id: PydanticObjectId = Field(
foreignKey={ foreignKey={
"reference": { "reference": {
"resource": "template/provision", "resource": "template/provision",

View File

@@ -12,11 +12,11 @@ class Registry:
self.instance = instance self.instance = instance
self.firm = firm self.firm = firm
self.current_firm = CurrentFirmModel.get(self.db) self.current_firm = CurrentFirmModel.get_current(self.db)
def set_user(self, user): def set_user(self, user):
for firm in user.firms: for firm in user.firms:
if firm.instance == self.instance and firm.firm == firm: if firm.instance == self.instance and firm.firm == self.firm:
self.user = user self.user = user
self.db.user = user self.db.user = user
return return

View File

@@ -9,9 +9,9 @@ from pydantic import BaseModel, Field, computed_field
class CrudDocument(BaseModel): class CrudDocument(BaseModel):
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: str = Field(nullable=False, title="Créé par") created_by: Optional[PydanticObjectId] = Field(default=None, title="Créé par")
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), nullable=False, title="Modifié le") updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), nullable=False, title="Modifié le")
updated_by: str = Field(nullable=False, title="Modifié par") updated_by: Optional[PydanticObjectId] = Field(default=None, title="Modifié par")
@property @property
def _id(self): def _id(self):
@@ -37,7 +37,7 @@ class CrudDocument(BaseModel):
@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.user, "updated_by":db.user} model_dict = create_schema.model_dump() | {"created_by": db.user.id, "updated_by": db.user.id}
document = cls.model_validate(model_dict).model_dump(mode="json") document = cls.model_validate(model_dict).model_dump(mode="json")
result = await cls._get_collection(db).insert_one(document) result = await cls._get_collection(db).insert_one(document)
@@ -66,8 +66,9 @@ class CrudDocument(BaseModel):
@classmethod @classmethod
async def update(cls, db, model, update_schema): async def update(cls, db, model, update_schema):
model_dict = update_schema.model_dump(mode="json") | {"updated_by": db.user.id}
update_query = { update_query = {
"$set": {field: value for field, value in update_schema.model_dump(mode="json").items() if field!= "id" } "$set": {field: value for field, value in model_dict.items() if field!= "id" }
} }
await cls._get_collection(db).update_one({"_id": model.id}, update_query) await cls._get_collection(db).update_one({"_id": model.id}, update_query)

View File

@@ -1,5 +1,6 @@
from typing import Any from typing import Any
from beanie import PydanticObjectId
from pydantic import Field from pydantic import Field
from firm.core.models import CrudDocument from firm.core.models import CrudDocument
@@ -8,12 +9,11 @@ from firm.entity.schemas import EntityIndividualCreate, EntityCorporationCreate
class CurrentFirmModel(CrudDocument): class CurrentFirmModel(CrudDocument):
instance: str instance: str = Field()
firm: str firm: str = Field()
name: str = Field(nullable=False) entity_id: PydanticObjectId = Field()
primary_color: str = Field()
# primary_color: str = Field() secondary_color: str = Field()
# secondary_color: str = Field()
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if isinstance(other, dict): if isinstance(other, dict):
@@ -21,10 +21,10 @@ class CurrentFirmModel(CrudDocument):
return super().__eq__(other) return super().__eq__(other)
def compute_label(self) -> str: def compute_label(self) -> str:
return self.name return f"{self.instance} / {self.firm}"
@classmethod @classmethod
async def get(cls, db): async def get_current(cls, db):
document = await cls._get_collection(db).find_one({}) document = await cls._get_collection(db).find_one({})
if not document: if not document:
return None return None
@@ -39,7 +39,15 @@ class CurrentFirmSchemaRead(Reader):
class CurrentFirmSchemaCreate(Writer): class CurrentFirmSchemaCreate(Writer):
corporation: EntityCorporationCreate = Field(title="Informations sur la firme") corporation: EntityCorporationCreate = Field(title="Informations sur la firme")
owner: EntityIndividualCreate = Field(title="Informations sur le dirigeant") owner: EntityIndividualCreate = Field(title="Informations sur le dirigeant")
position: str = Field(title="Poste")
primary_color: str = Field()
secondary_color: str = Field()
class CurrentFirmSchemaUpdate(Writer): class CurrentFirmSchemaUpdate(Writer):
pass pass
class Partner(CrudDocument):
user_id: PydanticObjectId = Field()
entity_id: PydanticObjectId = Field()

View File

@@ -1,22 +1,36 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from firm.core.depends import get_logged_tenant_db_cursor, get_uninitialized_tenant_db_cursor from firm.core.depends import get_authed_tenant_registry, get_uninitialized_registry
from firm.current_firm import CurrentFirmModel, CurrentFirmSchemaRead, CurrentFirmSchemaCreate, CurrentFirmSchemaUpdate from firm.current_firm import CurrentFirmModel, CurrentFirmSchemaRead, CurrentFirmSchemaCreate, CurrentFirmSchemaUpdate, Partner
from firm.entity.models import Entity, Employee
current_firm_router = APIRouter() 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(db=Depends(get_logged_tenant_db_cursor)) -> CurrentFirmSchemaRead: async def read(reg=Depends(get_authed_tenant_registry)) -> CurrentFirmSchemaRead:
return CurrentFirmSchemaRead.from_model(**CurrentFirmModel.get(db)) document = await CurrentFirmModel.get_current(reg.db)
return CurrentFirmSchemaRead.from_model(document)
@current_firm_router.post("/", response_description=f"Current Firm added to the database") @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: async def create(schema: CurrentFirmSchemaCreate, reg=Depends(get_uninitialized_registry)) -> CurrentFirmSchemaRead:
await schema.validate_foreign_key(db) owner_entity = await Entity.create(reg.db, schema.owner)
record = await CurrentFirmModel.create(db, schema) Partner.create(reg.db, Partner(user_id=reg.user.id, entity_id=owner_entity.id))
return CurrentFirmSchemaRead.from_model(record)
corporation_schema = schema.corporation
corporation_schema.entity_data.employees.append(Employee(entity_id=owner_entity.id, position=schema.position))
corp = await Entity.create(reg.db, corporation_schema)
document = await CurrentFirmModel.create(reg.db, CurrentFirmModel(
instance=reg.instance,
firm=reg.firm,
entity_id=corp.id,
primary_color=schema.primary_color,
secondary_color=schema.secondary_color,
))
return await CurrentFirmSchemaRead.from_model(document)
@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, db=Depends(get_logged_tenant_db_cursor)) -> CurrentFirmSchemaRead: async def update(schema: CurrentFirmSchemaUpdate, reg=Depends(get_authed_tenant_registry)) -> CurrentFirmSchemaRead:
record = await CurrentFirmModel.get(db) document = await CurrentFirmModel.get_current(reg.db)
record = await CurrentFirmModel.update(db, record, schema) document = await CurrentFirmModel.update(reg.db, document, schema)
return CurrentFirmSchemaRead.from_model(record) return CurrentFirmSchemaRead.from_model(document)

View File

@@ -2,7 +2,7 @@ from datetime import date, datetime
from typing import List, Literal, Optional from typing import List, Literal, Optional
from pydantic import Field, BaseModel from pydantic import Field, BaseModel
from beanie import Indexed from beanie import Indexed, PydanticObjectId
from firm.core.models import CrudDocument from firm.core.models import CrudDocument
from firm.core.filter import Filter, FilterSchema from firm.core.filter import Filter, FilterSchema
@@ -38,8 +38,9 @@ class Individual(EntityType):
class Employee(BaseModel): class Employee(BaseModel):
role: Indexed(str) = Field(title='Poste') position: Indexed(str) = Field(title='Poste')
entity_id: str = Field(foreignKey={ entity_id: PydanticObjectId = Field(
foreignKey={
"reference": { "reference": {
"resource": "entity", "resource": "entity",
"schema": "Entity", "schema": "Entity",

View File

@@ -1,6 +1,7 @@
from typing import List from typing import List
from html import unescape from html import unescape
from beanie import PydanticObjectId
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from firm.core.models import CrudDocument, RichtextMultiline, RichtextSingleline, DictionaryEntry from firm.core.models import CrudDocument, RichtextMultiline, RichtextSingleline, DictionaryEntry
@@ -8,7 +9,7 @@ from firm.core.filter import Filter, FilterSchema
class PartyTemplate(BaseModel): class PartyTemplate(BaseModel):
entity_id: str = Field( entity_id: PydanticObjectId = Field(
foreignKey={ foreignKey={
"reference": { "reference": {
"resource": "entity", "resource": "entity",
@@ -19,7 +20,7 @@ class PartyTemplate(BaseModel):
title="Partie" title="Partie"
) )
part: str = Field(title="Rôle") part: str = Field(title="Rôle")
representative_id: str = Field( representative_id: PydanticObjectId = Field(
foreignKey={ foreignKey={
"reference": { "reference": {
"resource": "entity", "resource": "entity",
@@ -61,7 +62,7 @@ class ProvisionTemplate(CrudDocument):
class ProvisionTemplateReference(BaseModel): class ProvisionTemplateReference(BaseModel):
provision_template_id: str = Field( provision_template_id: PydanticObjectId = Field(
foreignKey={ foreignKey={
"reference": { "reference": {
"resource": "template/provision", "resource": "template/provision",

View File

@@ -1,14 +1,15 @@
import validator from "@rjsf/validator-ajv8"; import validator from "@rjsf/validator-ajv8";
import Form from "@rjsf/mui"; import Form from "@rjsf/mui";
import { RegistryFieldsType, RegistryWidgetsType } from "@rjsf/utils"; import { RegistryFieldsType, RegistryWidgetsType, UiSchema } from "@rjsf/utils";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { jsonschemaProvider } from "../providers/jsonschema-provider"; import { jsonschemaProvider } from "../providers/jsonschema-provider";
import { useForm } from "@refinedev/core"; import { useForm } from "@refinedev/core";
import CrudTextWidget from "./widgets/crud-text-widget"; import CrudTextWidget from "./widgets/crud-text-widget";
import UnionEnumField from "./fields/union-enum"; import UnionEnumField from "./fields/union-enum";
type Props = { type CrudFormProps = {
schemaName: string, schemaName: string,
uiSchema?: UiSchema,
resource: string, resource: string,
id?: string, id?: string,
//onSubmit: (data: IChangeEvent, event: FormEvent<any>) => void //onSubmit: (data: IChangeEvent, event: FormEvent<any>) => void
@@ -23,7 +24,8 @@ const customFields: RegistryFieldsType = {
AnyOfField: UnionEnumField AnyOfField: UnionEnumField
} }
export const CrudForm: React.FC<Props> = ({ schemaName, resource, id, onSuccess }) => { export const CrudForm: React.FC<CrudFormProps> = (props) => {
const { schemaName, uiSchema, resource, id, onSuccess } = props;
const { onFinish, query, formLoading } = useForm({ const { onFinish, query, formLoading } = useForm({
resource: resource, resource: resource,
action: id === undefined ? "create" : "edit", action: id === undefined ? "create" : "edit",
@@ -55,6 +57,7 @@ export const CrudForm: React.FC<Props> = ({ schemaName, resource, id, onSuccess
return ( return (
<Form <Form
schema={schema} schema={schema}
uiSchema={uiSchema === undefined ? {} : uiSchema}
formData={record} formData={record}
onChange={(e) => setFormData(e.formData)} onChange={(e) => setFormData(e.formData)}
onSubmit={(e) => onFinish(e.formData)} onSubmit={(e) => onFinish(e.formData)}

View File

@@ -1,12 +1,16 @@
import {FormContextType, getTemplate, RJSFSchema, StrictRJSFSchema, WidgetProps} from "@rjsf/utils"; import {FormContextType, getTemplate, RJSFSchema, StrictRJSFSchema, WidgetProps} from "@rjsf/utils";
import ForeignKeyWidget from "./foreign-key"; import ForeignKeyWidget from "./foreign-key";
import Typography from "@mui/material/Typography";
export default function CrudTextWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( export default function CrudTextWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
props: WidgetProps<T, S, F> props: WidgetProps<T, S, F>
) { ) {
if (props.schema.hasOwnProperty("foreign_key")) { const { schema } = props;
if (schema.hasOwnProperty("foreign_key")) {
return (<ForeignKeyWidget {...props} />); return (<ForeignKeyWidget {...props} />);
} else if (schema.hasOwnProperty("const")) {
return <Typography >{schema.const as string}</Typography>;
} else { } else {
const { options, registry } = props; const { options, registry } = props;
const BaseInputTemplate = getTemplate<'BaseInputTemplate', T, S, F>('BaseInputTemplate', registry, options); const BaseInputTemplate = getTemplate<'BaseInputTemplate', T, S, F>('BaseInputTemplate', registry, options);

View File

@@ -49,7 +49,14 @@ const FirmInitForm = (props: FirmInitFormPros) => {
return ( return (
<> <>
<h1>Initialization of {`${currentFirm.instance} / ${currentFirm.firm}`}</h1> <h1>Initialization of {`${currentFirm.instance} / ${currentFirm.firm}`}</h1>
<CrudForm schemaName={"CurrentFirmSchemaCreate"} resource={`/firm/${currentFirm.instance}/${currentFirm.firm}`} />
<CrudForm
schemaName={"CurrentFirmSchemaCreate"}
resource={`firm/${currentFirm.instance}/${currentFirm.firm}/`}
uiSchema={{
corporation: {entity_data: {employees: {"ui:style": {"display": "none"}}}},
}}
/>
</> </>
) )
} }