Source code for cadrumo.application.modelo._official_box_advisory
"""Calculate-path collector for registry-authored official-box advisories.The collector is intentionally revision-driven: it scans the:class:`ModeloRevision` for ADVISORY ``implies_any_nonzero`` verificationpredicates and mirrors the same predicate shape as a non-blocking:class:`~cadrumo.application.aggregation.source_mesh.CalculationSourceDiagnostic` on thecalculate path. The verification predicate remains the single source of truthfor any total-to-official-box mapping, so calculate diagnostics and verifyfindings cannot drift: the fire condition is not restated here but taken from:func:`~cadrumo.application.modelo.verification_predicates.evaluate_advisory_predicate_fires`,the same evaluator the verify gate runs. That call is what makes the sentenceabove structurally true rather than a claim -- this collector once carried itsown copy of the antecedent/consequent test, which agreed with the verificationside only for as long as nobody edited either.Modelo 303's former post-2022 revision used this mechanism in Stage 1, whenledger-backed semantic totals could be positive while manual officialDiseño-de-Registros cuota boxes stayed zero. Stage 2 now projects those boxesfrom their semantic sources and retired the Stage-1 ADVISORY predicates, so thiscollector normally emits no M303 official-box diagnostics for that revision.See Also: :mod:`~cadrumo.application.modelo.calculation_diagnostics` Post-calculation coordinator that calls this collector with the engine casilla values. :mod:`~cadrumo.application.modelo.verification_actions` Verification predicate parser/evaluator whose ``implies_any_nonzero`` shape this collector mirrors."""from__future__importannotationsfromcollections.abcimportMappingfromdecimalimportDecimalfrom...core.casilla_idimportCasillaIdfrom...domain.calculations.registry.schemaimportModeloRevisionfrom..aggregation.source_meshimportCalculationSourceDiagnostic__all__=["collect_official_box_unpopulated_diagnostics"]
[docs]defcollect_official_box_unpopulated_diagnostics(revision:ModeloRevision,casilla_values:Mapping[CasillaId,Decimal],)->tuple[CalculationSourceDiagnostic,...]:"""Return advisories for ADVISORY ``implies_any_nonzero`` predicates that fire. A predicate fires when its antecedent (a computed total) is strictly positive while every listed consequent (the official numbered boxes) is zero. Each fired predicate yields one :class:`~cadrumo.application.aggregation.source_mesh.CalculationSourceDiagnostic` with ``reason = "official_box_unpopulated"``, naming the positive antecedent casilla and the unpopulated official boxes so the operator-facing surface can instruct the transcription. Args: revision: The :class:`ModeloRevision` whose ADVISORY ``implies_any_nonzero`` predicates are evaluated. If the revision has retired those predicates, no diagnostic is emitted. casilla_values: The computed engine values keyed by :class:`CasillaId` and used to test the predicates, so both the semantic antecedent (e.g. ``iva.cuota-devengada-total``) and the official box ids (e.g. ``09``) resolve. See Also: ``cadrumo.application.modelo.verification_actions._evaluate_predicate_expression``: Verification-side evaluator for the same predicate DSL. """# Lazy imports avoid the calculate/verification action cycle. The domain# registry owns predicate syntax; the verification module owns evaluation.from...domain.calculations.registry.schema_verificationimport(VerificationPredicateOperator,parse_verification_predicate_expression,)from.verification_predicatesimportevaluate_advisory_predicate_firesdiagnostics:list[CalculationSourceDiagnostic]=[]forpredicateinrevision.verification_predicates:ifpredicate.finding_kind!="ADVISORY":continueparsed=parse_verification_predicate_expression(predicate.expression)ifparsedisNoneorparsed.operatorisnotVerificationPredicateOperator.IMPLIES_ANY_NONZERO:continueids=parsed.casilla_idsiflen(ids)<2:continue# The fire condition is the verification side's, called rather than# restated: this collector and the verify gate must answer identically# for the same casilla values, and a second copy of the antecedent /# consequent test is how they would stop doing so. The regex and the id# parser above are still needed here to name the boxes in the message.ifnotevaluate_advisory_predicate_fires(predicate.expression,casilla_values):continueantecedent_id=ids[0]consequent_ids=ids[1:]antecedent=casilla_values.get(antecedent_id,Decimal(0))diagnostics.append(CalculationSourceDiagnostic(reason="official_box_unpopulated",source_kind="official_diseno_boxes",message=(f"computed total {antecedent_id!r} = {antecedent} is positive but the official "f"Diseño-de-Registros boxes {consequent_ids!r} are all zero; the calculate path does "f"not auto-populate the official numbered boxes — transcribe the cuota before filing"),casilla_id=antecedent_id,),)returntuple(diagnostics)