Source code for cadrumo.domain.transactions.cash_accounting_validation

"""Private cash-accounting transaction invariant helpers."""

from __future__ import annotations

from datetime import date
from decimal import Decimal

from ...core.decimal.constants import ZERO
from ..calculations.registry.iva_schema_vocabulary import require_iva_cash_accounting_treatment
from ..iva.schema import (
    IvaCashAccountingPaymentEvidence,
    IvaCashAccountingTreatment,
    is_iva_cash_accounting_none,
    is_iva_cash_accounting_supplier_regime,
)
from .enums import TransactionDirection
from .errors import TransactionValidationError


def _required_operation_date(operation_date: date | None) -> date:
    """Return the operation date or raise the regime's required-field refusal."""
    if operation_date is None:
        raise TransactionValidationError(
            "operation_date is required when cash_accounting_treatment is not NONE",
        )
    return operation_date


def _require_payment_evidence(
    payment_evidence: tuple[IvaCashAccountingPaymentEvidence, ...],
) -> None:
    """Require at least one settlement event for a cash-accounting row."""
    if not payment_evidence:
        raise TransactionValidationError(
            "cash_accounting_payment_evidence is required for cash-accounting operations; "
            "wholly unpaid fallback-only operations are not yet represented",
        )


def _require_tax_substrate(
    taxable_base: Decimal | None,
    iva_amount: Decimal | None,
) -> tuple[Decimal, Decimal]:
    """Return the base and IVA facts needed to cap settlement evidence."""
    if taxable_base is None or iva_amount is None:
        raise TransactionValidationError(
            "cash-accounting operations require taxable_base and iva_amount facts",
        )
    return taxable_base, iva_amount


def _require_supplier_regime_direction(
    *,
    treatment: IvaCashAccountingTreatment,
    direction: TransactionDirection,
) -> None:
    """Keep supplier-regime treatment on received/purchase rows only."""
    if is_iva_cash_accounting_supplier_regime(treatment) and direction is not TransactionDirection.OUTGOING:
        raise TransactionValidationError(
            "supplier-regime cash-accounting treatment is only valid on received/purchase rows",
        )


def _validate_payment_totals(
    *,
    payment_evidence: tuple[IvaCashAccountingPaymentEvidence, ...],
    taxable_base: Decimal,
    iva_amount: Decimal,
    recargo_amount: Decimal | None,
) -> None:
    """Ensure settlement evidence cannot exceed the transaction substrate."""
    total_base = sum((evidence.taxable_base for evidence in payment_evidence), ZERO)
    total_iva = sum((evidence.iva_amount for evidence in payment_evidence), ZERO)
    total_recargo = sum(
        (evidence.recargo_amount for evidence in payment_evidence),
        ZERO,
    )
    recargo = recargo_amount or ZERO
    if total_base > taxable_base or total_iva > iva_amount or total_recargo > recargo:
        raise TransactionValidationError(
            "cash_accounting_payment_evidence totals must not exceed taxable_base, iva_amount, or recargo_amount",
        )


def _validate_payment_dates(
    *,
    payment_evidence: tuple[IvaCashAccountingPaymentEvidence, ...],
    operation_date: date,
) -> None:
    """Reject settlement evidence after the statutory fallback date."""
    fallback_date = date(operation_date.year + 1, 12, 31)
    if any(evidence.payment_date > fallback_date for evidence in payment_evidence):
        raise TransactionValidationError(
            "cash_accounting_payment_evidence cannot fall after the 31 December statutory fallback date",
        )


[docs] def validate_cash_accounting_axis( *, treatment: IvaCashAccountingTreatment, operation_date: date | None, payment_evidence: tuple[IvaCashAccountingPaymentEvidence, ...], taxable_base: Decimal | None, iva_amount: Decimal | None, recargo_amount: Decimal | None, direction: TransactionDirection, ) -> None: """Validate timing, direction, substrate, and settlement evidence coupling.""" treatment = require_iva_cash_accounting_treatment(treatment) if is_iva_cash_accounting_none(treatment): if payment_evidence: raise TransactionValidationError( "cash_accounting_payment_evidence requires a non-NONE cash_accounting_treatment", ) return required_date = _required_operation_date(operation_date) _require_payment_evidence(payment_evidence) taxable_base, iva_amount = _require_tax_substrate(taxable_base, iva_amount) _require_supplier_regime_direction(treatment=treatment, direction=direction) _validate_payment_totals( payment_evidence=payment_evidence, taxable_base=taxable_base, iva_amount=iva_amount, recargo_amount=recargo_amount, ) _validate_payment_dates(payment_evidence=payment_evidence, operation_date=required_date)