Better feedback on draft publication

This commit is contained in:
2023-03-10 17:27:14 +01:00
parent a52435d443
commit 1d3918db87
10 changed files with 198 additions and 73 deletions

View File

@@ -6,7 +6,7 @@ from ..core.routes import get_crud_router
from .routes_draft import draft_router
from .print import print_router
from .models import Contract, ContractDraft, Party, replace_variables_in_value
from .models import Contract, ContractDraft, ContractDraftStatus, Party, replace_variables_in_value
from .schemas import ContractCreate, ContractRead, ContractUpdate
from ..entity.models import Entity
@@ -64,6 +64,8 @@ async def create(item: ContractCreate, user=Depends(get_current_user)) -> dict:
contract_dict['provisions'] = provisions
o = await Contract(**contract_dict).create()
await draft.update({"$set": {"status": ContractDraftStatus.published}})
return {"message": "Contract Successfully created", "id": o.id}
@@ -89,11 +91,11 @@ async def affix_signature(signature_id: str, signature_file: UploadFile = File(.
if signature.signature_affixed:
raise HTTPException(status_code=400, detail="Signature already affixed")
with open("media/signatures/{}.png".format(signature_id), "wb") as buffer:
with open(f'media/signatures/{signature_id}.png', "wb") as buffer:
shutil.copyfileobj(signature_file.file, buffer)
update_query = {"$set": {
'parties.{}.signature_affixed'.format(signature_index): True
f'parties.{signature_index}.signature_affixed': True
}}
signature.signature_affixed = True
if contract.is_signed():

View File

@@ -19,7 +19,7 @@ class ContractStatus(str, Enum):
class ContractDraftStatus(str, Enum):
in_progress = 'in_progress'
ready = 'ready'
created = 'created'
published = 'published'
class DraftParty(BaseModel):
@@ -29,7 +29,8 @@ class DraftParty(BaseModel):
"resource": "entity",
"schema": "Entity",
}
}
},
default=""
)
part: str
representative_id: str = Field(
@@ -59,8 +60,8 @@ class Party(BaseModel):
class ProvisionGenuine(BaseModel):
type: Literal['genuine'] = 'genuine'
title: str = RichtextSingleline(props={"parametrized": True})
body: str = RichtextMultiline(props={"parametrized": True})
title: str = RichtextSingleline(props={"parametrized": True}, default="")
body: str = RichtextMultiline(props={"parametrized": True}, default="")
class ContractProvisionTemplateReference(BaseModel):
@@ -73,7 +74,8 @@ class ContractProvisionTemplateReference(BaseModel):
"displayedFields": ['title', 'body']
},
},
props={"parametrized": True}
props={"parametrized": True},
default=""
)
@@ -98,6 +100,7 @@ class ContractDraft(CrudDocument):
format="dictionary",
)
status: ContractDraftStatus = ContractDraftStatus.in_progress
todo: List[str] = []
class Settings(CrudDocument.Settings):
bson_encoders = {
@@ -106,6 +109,40 @@ class ContractDraft(CrudDocument):
hour=0, minute=0, second=0)
}
async def check_is_ready(self):
if self.status == ContractDraftStatus.published:
return
self.todo = []
if len(self.parties) < 2:
self.todo.append('Contract must have at least two parties')
if len(self.provisions) < 1:
self.todo.append('Contract must have at least one provision')
for p in self.parties:
if not p.entity_id:
self.todo.append('All parties must have an associated entity`')
for p in self.provisions:
if p.provision.type == "genuine" and not (p.provision.title and p.provision.body):
self.todo.append('Empty genuine provision')
elif p.provision.type == "template" and not p.provision.provision_template_id:
self.todo.append('Empty template provision')
for v in self.variables:
if not (v.key and v.value):
self.todo.append('Empty variable')
if self.todo:
self.status = ContractDraftStatus.in_progress
else:
self.status = ContractDraftStatus.ready
await self.update({"$set": {
"status": self.status,
"todo": self.todo
}})
class Contract(CrudDocument):
name: str

View File

@@ -84,8 +84,8 @@ async def preview_draft(draft_id: str) -> str:
return await render_print('localhost', draft)
@print_router.get("/preview/{signature_id}", response_class=HTMLResponse)
async def preview_contract(signature_id: str) -> str:
@print_router.get("/preview/signature/{signature_id}", response_class=HTMLResponse)
async def preview_contract_by_signature(signature_id: str) -> str:
contract = await Contract.find_by_signature_id(signature_id)
for p in contract.parties:
if p.signature_affixed:
@@ -94,6 +94,16 @@ async def preview_contract(signature_id: str) -> str:
return await render_print('localhost', contract)
@print_router.get("/preview/{contract_id}", response_class=HTMLResponse)
async def preview_contract(contract_id: str) -> str:
contract = await Contract.get(contract_id)
for p in contract.parties:
if p.signature_affixed:
p.signature_png = retrieve_signature_png(f'media/signatures/{p.signature_uuid}.png')
return await render_print('localhost', contract)
@print_router.get("/pdf/{contract_id}", response_class=FileResponse)
async def create_pdf(contract_id: str) -> str:
contract = await Contract.get(contract_id)

View File

@@ -1,7 +1,47 @@
from fastapi import APIRouter
from beanie import PydanticObjectId
from fastapi import HTTPException, Depends
from ..core.routes import get_crud_router
from .models import ContractDraft
from ..user.manager import get_current_user
from .models import ContractDraft, ContractDraftStatus
from .schemas import ContractDraftCreate, ContractDraftRead, ContractDraftUpdate
draft_router = get_crud_router(ContractDraft, ContractDraftCreate, ContractDraftRead, ContractDraftUpdate)
del(draft_router.routes[0])
del(draft_router.routes[2])
@draft_router.post("/", response_description="Contract Draft added to the database")
async def create(item: ContractDraftCreate, user=Depends(get_current_user)) -> dict:
await item.validate_foreign_key()
o = await ContractDraft(**item.dict()).create()
await o.check_is_ready()
return {"message": "Contract Draft added successfully", "id": o.id}
@draft_router.put("/{id}", response_description="Contract Draft record updated")
async def update(id: PydanticObjectId, req: ContractDraftUpdate, user=Depends(get_current_user)) -> ContractDraftRead:
req = {k: v for k, v in req.dict().items() if v is not None}
update_query = {"$set": {
field: value for field, value in req.items()
}}
item = await ContractDraft.get(id)
if not item:
raise HTTPException(
status_code=404,
detail="Contract Draft record not found!"
)
if item.status == ContractDraftStatus.published:
raise HTTPException(
status_code=400,
detail="Contract Draft has already been published"
)
await item.update(update_query)
await item.check_is_ready()
return ContractDraftRead(**item.dict())

View File

@@ -3,7 +3,7 @@ from typing import List
from pydantic import BaseModel, Field
from .models import ContractDraft, DraftProvision, Party, Contract
from .models import ContractDraft, DraftProvision, DraftParty, Contract
from ..entity.models import Entity
from ..core.schemas import Writer
@@ -17,14 +17,12 @@ class ContractDraftRead(ContractDraft):
class ContractDraftCreate(Writer):
name: str
title: str
parties: List[Party]
parties: List[DraftParty]
provisions: List[DraftProvision]
variables: List[DictionaryEntry] = Field(
default=[],
format="dictionary",
)
location: str = ""
date: datetime.date = datetime.date(1, 1, 1)
async def validate_foreign_key(self):
return