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

@@ -67,6 +67,9 @@ class RemissionPractice(Base):
reviewed_by = Column(Integer, nullable=True)
rejection_reason = Column(Text, nullable=True)
approved_remission = Column(Numeric(14, 2), nullable=True)
instructor_final_notes = Column(Text, nullable=True)
instructor_checklist = Column(JSONB, nullable=True, default=dict)
verbale_date = Column(Date, nullable=True)
created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now())
updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
@@ -101,6 +104,17 @@ class RemissionInvoice(Base):
total = Column(Numeric(14, 2), nullable=False)
pdf_filename = Column(String(512), nullable=True) # per ora solo nome, upload vero dopo
# Campi istruttoria (dual declared/verified)
taxable_verified = Column(Numeric(14, 2), nullable=True)
vat_verified = Column(Numeric(14, 2), nullable=True)
total_verified = Column(Numeric(14, 2), nullable=True)
verification_status = Column(String(16), nullable=False, default="PENDING")
# PENDING | AMMESSA | PARZIALE | RESPINTA
verification_notes = Column(Text, nullable=True)
date_checks = Column(JSONB, nullable=True, default=dict)
verified_by = Column(Integer, nullable=True)
verified_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now())
practice = relationship("RemissionPractice", back_populates="invoices")
@@ -127,6 +141,13 @@ class RemissionUlaEmployee(Base):
supporting_doc_type = Column(String(64), nullable=True)
supporting_doc_filename = Column(String(512), nullable=True)
# Campi istruttoria
fte_pct_verified = Column(Numeric(5, 4), nullable=True)
verification_status = Column(String(16), nullable=False, default="PENDING")
verification_notes = Column(Text, nullable=True)
verified_by = Column(Integer, nullable=True)
verified_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now())
practice = relationship("RemissionPractice", back_populates="ula_employees")
@@ -148,6 +169,13 @@ class RemissionDocument(Base):
expires_at = Column(Date, nullable=True)
notes = Column(Text, nullable=True)
# Campi istruttoria
verification_status = Column(String(16), nullable=False, default="PENDING")
# PENDING | VALIDO | NON_VALIDO | SCADUTO
verification_notes = Column(Text, nullable=True)
verified_by = Column(Integer, nullable=True)
verified_at = Column(DateTime(timezone=True), nullable=True)
practice = relationship("RemissionPractice", back_populates="documents")