feat(v2): multi-tranche DB schema + gate cumulativo 5 voci Cecilia
A1 migrations.py: - remission_practice DROP uq_application + ADD sequence_number/period_label/suggested_instructor_id - UNIQUE composita (application_id, sequence_number) - partial index idx_remission_practice_unassigned su assigned_instructor_id NULL - nuova tabella remission_custom_check_value (storage_path/mime/size/sha256 allineata adapter) A2 models.py + templates.py: - RemissionPractice: UniqueConstraint composita, campi multi-tranche, relationship custom_checks - classe RemissionCustomCheckValue - RESTART_TEMPLATE schema_version=2, max_tranches=2, custom_checks esempio (antiriciclaggio required no-doc, polizza_fidejussoria optional con-doc) - upgrade_schema_to_v2 idempotente per snapshot v1 esistenti A3 _compute_gate_check(db, practice) CUMULATIVO: - max_remission_global = min(cap_pct * erogato, cap_abs) - already_approved = func.sum(approved_remission) su tranche APPROVED precedenti dello stesso application_id con sequence_number < corrente - max_remission_this_tranche = max(0, global - already_approved) - pre_check_admissible = min(grand_total_declared, this_tranche) [voce 2 Cecilia] - remission_due = min(effective_total, this_tranche) - residuo_da_restituire = erogato - already_approved - remission_due (cumulativo) - output totals esteso: sequence_number, tranches_count, tranches_max - signature (db, practice) - aggiornati 6 call site in practices/instructor/verbale Test su NAPOLI SAS: erogato 17K, cap 8500, tranche 1 approvata 467.14EUR, tranche 2 vuota -> residuo disponibile 8032.86EUR, residuo_da_restituire 16532.86EUR.
This commit is contained in:
@@ -40,8 +40,10 @@ class RemissionSchemaOut(BaseModel):
|
||||
# ====================== Pratica di rendicontazione (beneficiario) ======================
|
||||
|
||||
class PracticeStartRequest(BaseModel):
|
||||
"""Input minimo per avviare una pratica: solo application_id. Il resto viene dal DB."""
|
||||
"""Input per avviare una (nuova) pratica o tranche."""
|
||||
application_id: int
|
||||
period_label: Optional[str] = None # es "I trimestre 2021" — libero
|
||||
copy_ula_from_previous: bool = True # ignorato se e la prima tranche
|
||||
|
||||
|
||||
class PracticeUpdate(BaseModel):
|
||||
@@ -160,6 +162,11 @@ class PracticeOut(BaseModel):
|
||||
instructor_checklist: Optional[dict] = None
|
||||
verbale_date: Optional[date] = None
|
||||
|
||||
# v2 multi-tranche
|
||||
sequence_number: int = 1
|
||||
period_label: Optional[str] = None
|
||||
suggested_instructor_id: Optional[int] = None
|
||||
|
||||
invoices: List[InvoiceOut] = []
|
||||
ula_employees: List[UlaEmployeeOut] = []
|
||||
documents: List[DocumentOut] = []
|
||||
@@ -178,6 +185,11 @@ class PracticeListItem(BaseModel):
|
||||
created_at: datetime
|
||||
submitted_at: Optional[datetime] = None
|
||||
|
||||
# v2 multi-tranche
|
||||
sequence_number: int = 1
|
||||
period_label: Optional[str] = None
|
||||
suggested_instructor_id: Optional[int] = None
|
||||
|
||||
# campi denormalizzati aggiunti a runtime
|
||||
call_name: Optional[str] = None
|
||||
company_name: Optional[str] = None
|
||||
@@ -237,6 +249,9 @@ class InstructorQueueItem(BaseModel):
|
||||
id: UUID
|
||||
call_id: int
|
||||
application_id: int
|
||||
sequence_number: int = 1
|
||||
period_label: Optional[str] = None
|
||||
suggested_instructor_id: Optional[int] = None
|
||||
company_id: int
|
||||
status: str
|
||||
amount_erogato: Decimal
|
||||
@@ -288,3 +303,76 @@ class ApiResponse(BaseModel):
|
||||
status: str = "SUCCESS"
|
||||
message: Optional[str] = None
|
||||
data: Optional[Any] = None
|
||||
|
||||
|
||||
# ====================== v2 Custom checks ======================
|
||||
|
||||
class CustomCheckDeclareBody(BaseModel):
|
||||
beneficiary_declared: bool
|
||||
|
||||
|
||||
class CustomCheckVerifyBody(BaseModel):
|
||||
verification_status: str # PENDING | VALIDO | NON_VALIDO
|
||||
verification_notes: Optional[str] = None
|
||||
|
||||
|
||||
class CustomCheckOut(BaseModel):
|
||||
"""Vista merged di definition (da schema) + value (dal DB)."""
|
||||
code: str
|
||||
label: str
|
||||
description: Optional[str] = None
|
||||
requires_document: bool = False
|
||||
required: bool = False
|
||||
# valori
|
||||
beneficiary_declared: bool = False
|
||||
declared_at: Optional[datetime] = None
|
||||
filename_original: Optional[str] = None
|
||||
storage_path: Optional[str] = None
|
||||
size_bytes: Optional[int] = None
|
||||
document_uploaded_at: Optional[datetime] = None
|
||||
verification_status: str = "PENDING"
|
||||
verification_notes: Optional[str] = None
|
||||
verified_by: Optional[int] = None
|
||||
verified_at: Optional[datetime] = None
|
||||
|
||||
|
||||
# ====================== v2 Reassign istruttore ======================
|
||||
|
||||
class PracticeReassignBody(BaseModel):
|
||||
new_instructor_id: Optional[int] = None # None = unassign ritorno in coda
|
||||
reassignment_reason: Optional[str] = None
|
||||
|
||||
|
||||
# ====================== v2 Tranches ======================
|
||||
|
||||
class ApplicationTranchesSummary(BaseModel):
|
||||
"""Riepilogo pratiche/tranche per una application."""
|
||||
application_id: int
|
||||
call_id: int
|
||||
call_name: Optional[str] = None
|
||||
company_id: int
|
||||
company_name: Optional[str] = None
|
||||
amount_erogato: float
|
||||
max_tranches: int = 1
|
||||
# summary tranche esistenti
|
||||
tranches: List[PracticeListItem] = []
|
||||
# stato apertura nuova tranche
|
||||
can_start_new: bool = False
|
||||
start_blocked_reason: Optional[str] = None
|
||||
# importi cumulativi
|
||||
already_approved_sum: float = 0
|
||||
max_remission_global: float = 0
|
||||
max_remission_next_tranche: float = 0
|
||||
|
||||
|
||||
class CopyUlaOption(BaseModel):
|
||||
"""Dipendente copiabile da tranche precedente."""
|
||||
codice_fiscale: str
|
||||
full_name: str
|
||||
contract_type: str
|
||||
role_description: Optional[str] = None
|
||||
fte_pct: float
|
||||
period_start_date: date
|
||||
period_end_date: date
|
||||
supporting_doc_type: Optional[str] = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user