feat(istruttoria): verifica riga-per-riga con dual declared/verified

Replica il workflow del foglio Excel originale (REMISSIONE_DEL_DEBITO_5888.xlsm).
Istruttore ora verifica ogni fattura, ogni dipendente ULA, ogni documento singolarmente
invece di accettare/respingere la pratica intera.

Modello dati - nuove colonne su 3 tabelle:
- remission_invoice: taxable_verified, vat_verified, total_verified,
  verification_status (PENDING/AMMESSA/PARZIALE/RESPINTA), verification_notes,
  date_checks (JSONB con invoice_in_period/payment_in_period), verified_by, verified_at
- remission_ula_employee: fte_pct_verified, verification_status, verification_notes,
  verified_by, verified_at
- remission_document: verification_status (PENDING/VALIDO/NON_VALIDO/SCADUTO),
  verification_notes, verified_by, verified_at
- remission_practice: instructor_final_notes, instructor_checklist (JSONB 3 gate SI/NO),
  verbale_date

Nuovi endpoint:
- PUT /instructor/{id}/invoices/{inv_id}/verify (status + rettifica importi + note)
- PUT /instructor/{id}/ula-employees/{emp_id}/verify (rettifica FTE + note)
- PUT /instructor/{id}/documents/{doc_code}/verify (VALIDO/NON_VALIDO/SCADUTO + note)
- PUT /instructor/{id}/final-notes (note sintetiche + checklist)

Ricalcolo gate_check dual track:
- grand_total_declared: sempre (importo richiesto dal beneficiario)
- grand_total_verified: somma solo fatture AMMESSA/PARZIALE (se PARZIALE usa verified)
- remission_due: usa verified se any_verified=True, altrimenti declared (backward compat)
- residuo_da_restituire: amount_erogato - remission_due
- flag any_verified e all_verified per gating decisione finale

_auto_check_dates: fattura in periodo? pagamento in periodo?
Legge period_start e period_end da schema.gate_rules (superadmin editor).

Template: aggiunto period_start/period_end_date come campi 'editable_by superadmin'
nella sezione general static_fields.

Schema editor FE (BandoRendicontazioneSchemaEdit): aggiunto Calendar period_start
accanto a period_end in section gate rules. period_start_rule dropdown per logica
(erogato_date|fixed) resta; period_start data fissa usata dal check.
This commit is contained in:
BFLOWS
2026-04-18 11:03:15 +02:00
parent 26fbc03871
commit f9f543b008
5 changed files with 312 additions and 12 deletions

View File

@@ -68,6 +68,15 @@ class InvoiceOut(InvoiceCreate):
id: UUID
practice_id: UUID
created_at: datetime
# istruttoria
taxable_verified: Optional[Decimal] = None
vat_verified: Optional[Decimal] = None
total_verified: Optional[Decimal] = None
verification_status: str = "PENDING"
verification_notes: Optional[str] = None
date_checks: Optional[dict] = None
verified_by: Optional[int] = None
verified_at: Optional[datetime] = None
model_config = {"from_attributes": True}
@@ -89,6 +98,12 @@ class UlaEmployeeOut(UlaEmployeeCreate):
id: UUID
practice_id: UUID
created_at: datetime
# istruttoria
fte_pct_verified: Optional[Decimal] = None
verification_status: str = "PENDING"
verification_notes: Optional[str] = None
verified_by: Optional[int] = None
verified_at: Optional[datetime] = None
model_config = {"from_attributes": True}
@@ -110,6 +125,11 @@ class DocumentOut(BaseModel):
uploaded_at: Optional[datetime] = None
expires_at: Optional[date] = None
notes: Optional[str] = None
# istruttoria
verification_status: str = "PENDING"
verification_notes: Optional[str] = None
verified_by: Optional[int] = None
verified_at: Optional[datetime] = None
model_config = {"from_attributes": True}
@@ -130,6 +150,16 @@ class PracticeOut(BaseModel):
updated_at: datetime
submitted_at: Optional[datetime] = None
# istruttoria
assigned_instructor_id: Optional[int] = None
reviewed_at: Optional[datetime] = None
reviewed_by: Optional[int] = None
rejection_reason: Optional[str] = None
approved_remission: Optional[Decimal] = None
instructor_final_notes: Optional[str] = None
instructor_checklist: Optional[dict] = None
verbale_date: Optional[date] = None
invoices: List[InvoiceOut] = []
ula_employees: List[UlaEmployeeOut] = []
documents: List[DocumentOut] = []
@@ -223,6 +253,35 @@ class InstructorQueueItem(BaseModel):
model_config = {"from_attributes": True}
# Verifica singola fattura
class InvoiceVerifyBody(BaseModel):
verification_status: str # AMMESSA | PARZIALE | RESPINTA
taxable_verified: Optional[Decimal] = None
vat_verified: Optional[Decimal] = None
total_verified: Optional[Decimal] = None
verification_notes: Optional[str] = None
# Verifica singolo dipendente ULA
class UlaVerifyBody(BaseModel):
verification_status: str # AMMESSA | PARZIALE | RESPINTA
fte_pct_verified: Optional[Decimal] = None
verification_notes: Optional[str] = None
# Verifica singolo documento
class DocumentVerifyBody(BaseModel):
verification_status: str # VALIDO | NON_VALIDO | SCADUTO
verification_notes: Optional[str] = None
# Note finali istruttore + checklist
class InstructorFinalNotesBody(BaseModel):
instructor_final_notes: Optional[str] = None
instructor_checklist: Optional[dict] = None
# ====================== Wrapper ======================
class ApiResponse(BaseModel):