cadrumo.domain.contribuyente.descendant_facts module

Helpers for persisting and reloading DescendantInfo as profile facts.

DescendantInfo records are stored as individual profile facts under the renta_family.descendiente.{n}.{field} key hierarchy. One aggregate — the row count — is stored alongside them so the registry binding resolver can look it up by a simple profile_key selector. Aggregates the ENGINE derives are not stored here; see the note below the fact table.

Stored fact paths per descendant (n = 0-based index):

renta_family.descendiente.{n}.birth_date              ISO-8601 date string
renta_family.descendiente.{n}.relacion                DescendantRelacion token (absent means ordinary)
renta_family.descendiente.{n}.inscripcion_registro_civil
                                                      ISO-8601 date string or absent
renta_family.descendiente.{n}.acogimiento_resolucion  ISO-8601 date string or absent
renta_family.descendiente.{n}.fallecimiento          ISO-8601 date string or absent (absent means the
                                                      descendant did not die)
renta_family.descendiente.{n}.discapacidad            registry-declared grade token or absent
renta_family.descendiente.{n}.convivencia             "true" / "false"
renta_family.descendiente.{n}.dependencia_economica   "true" / "false" or absent (absent means unset)
renta_family.descendiente.{n}.custodia_compartida     "true" / "false" (absent means False)
renta_family.descendiente.{n}.rentas_anuales          decimal euros or absent (absent means undeclared)
renta_family.descendiente.{n}.declaracion_propia      "true" / "false" (absent means False)
renta_family.descendiente.{n}.prorrata_minimo         "true" / "false" or absent (absent means unanswered)
renta_family.descendiente.{n}.meses_madre_trabajo     "MM;MM..." ascending, zero-padded
                                                      (absent means no qualifying months)
renta_family.descendiente.{n}.alta_posterior_nacimiento_mes
                                                      "1".."12" or absent (absent means no post-birth
                                                      alta increment declared)
renta_family.descendiente.{n}.gastos_guarderia        non-negative integer euros (absent means 0)
renta_family.descendiente.{n}.gastos_guarderia_mensuales
                                                      canonical MM:AMOUNT[;MM:AMOUNT...] map or absent
renta_family.descendiente.{n}.nif                     NIF string or absent

Aggregate facts stored:

renta_family.descendientes_count               int count

The Art. 81.2 guardería sum (renta_family.gastos_guarderia_reales_{year}) is deliberately NOT stored here, and must not be re-added. It is a DERIVED path: the calculate-time injector recomputes it from the per-child spend above — through the canonical record, so the annual figure and the monthly map are weighed by the same Art. 81.2 month rules — and overwrites whatever the index holds, precisely so an operator’s number can never be substituted for the law’s. The profile write door refuses that path outright, so projecting it from here would refuse the whole batch rather than persist a second, divergent copy.

descendant_facts_from_list(descendientes, *, authority=None)[source]

Return a list of (path, canonical-value-string) tuples for all DescendantInfo entries.

The caller converts these to domain.user_profile.values.UserProfileFact records; this function only computes the canonical key-value pairs.

Return type:

list[tuple[str, str]]

Parameters:
class RelacionKwarg[source]

Bases: TypedDict

The single optional constructor keyword relacion_kwarg() may supply.

A plain mapping loses which keyword is present, so splatting it into a constructor reported every unrelated field as mistyped. Declaring the one key it can carry keeps the splat precise.

relacion: DescendantRelacion
relacion_kwarg(relacion)[source]

Render an optional relación as the constructor keyword, OMITTING it when unstated.

“Unstated” and “ordinary descendant” are different inputs and the record treats them differently: an unstated relación carrying a Registro Civil inscription is read as an adoption, while an explicitly-ordinary one carrying the same date is a contradiction the coherence rule refuses. The only way to express “unstated” to a pydantic constructor is to leave the keyword out, so every door routes through this rather than inventing a sentinel — and the type checker sees a plain DescendantRelacion at each call site.

Return type:

RelacionKwarg

Parameters:

relacion (DescendantRelacion | None)

descendant_list_from_facts(facts, *, authority=None)[source]

Reconstruct a tuple of DescendantInfo from a flat profile-fact dict.

facts is a {path: canonical-value-string} mapping. Only renta_family.descendiente.* paths are consumed; all others are ignored.

Entries are sorted by index so the reconstructed tuple preserves the original insertion order.

Return type:

tuple[DescendantInfo, ...]

Returns:

Tuple of DescendantInfo reconstructed from the profile facts.

Parameters:
parse_descendiente_flag(raw, *, authority=None)[source]

Parse a --descendiente NACIMIENTO=YYYY-MM-DD,... flag value.

Accepted keys (case-insensitive):

NACIMIENTO=YYYY-MM-DD  (required) birth date
RELACION=descendiente|adoptado|acogimiento_preadoptivo_o_permanente|
         acogimiento_temporal|tutela|guarda_y_custodia_judicial
                       (optional, default descendiente) Art. 58.1 / 58.2
                       relationship. A temporal acogimiento takes the
                       tranches and NOT the under-three increase. A
                       judicial guarda y custodia takes the tranches and
                       neither the increase nor the Art. 81.1 deducción
                       por maternidad, which excludes it by name.
INSCRIPCION=YYYY-MM-DD (optional) Registro Civil inscription of the
                       adoption, or the resolución judicial o
                       administrativa where inscription is not required
                       — the Art. 58.2 anchor for RELACION=adoptado.
                       Supplying it without RELACION reads as adoptado.
ACOGIMIENTO=YYYY-MM-DD (optional) first ENTITLING acogimiento resolución
                       — the Art. 58.2 anchor for a preadoptivo or
                       permanente placement, and retained on an adoptado
                       record so a fostered-then-adopted child's window
                       is capped at three periods rather than restarted.
DISCAPACIDAD=<registry-declared-grade>   (optional) discapacidad grade
CONVIVENCIA=true|false (optional, default true) cohabitation flag
DEPENDENCIA=true|false (optional) the taxpayer contributes to this
                       descendant's upkeep without cohabiting. Omit to
                       leave UNSET, which never assimilates; only an
                       explicit true does, and only when no anualidades
                       are declared.
CUSTODIA=true|false    (optional, default false) custodia compartida (Art. 61 LIRPF)
RENTAS=N               (optional) annual rentas excluding exempt income — Art. 58.1 ceiling
DECLARACION_PROPIA=true|false
                       (optional, default false) descendant files their own IRPF return
                       — combines with RENTAS for the Art. 61 norma 2ª exclusion
PRORRATA=true|false    (optional) explicit Art. 61 norma 1ª answer: is another
                       contribuyente also entitled to this descendant's mínimo?
                       Omit to let the engine derive it from profile signals.
MESES_TRABAJO=0..12    (optional, default 0) months mother worked — Art. 81 deducción maternidad
ALTA_POSTERIOR_MES=1..12
                       (optional) calendar month in which a mother not registered with the
                       Seguridad Social or a mutualidad at the birth completed the 30-day
                       minimum contribution period Art. 81.1 requires for the post-birth alta
                       route. Adds the 150 euro completion-month increment (raising this
                       child's cap to 1.350 euros) for filing years from 2023 only. Omit for
                       the ordinary case.
SEGUNDO_CICLO_INFANTIL_INICIO_MES=1..12
                       (optional) the month in which the child may begin
                       the second cycle of educación infantil. It bounds
                       Art. 81.2 guardería spend in the year the child
                       turns three.
GASTOS_GUARDERIA=N     (optional, default 0) actual guardería euros — Art. 81.2 incremento 0613,
                       as an ANNUAL total. Sufficient only while the
                       child is under three for the whole period.
GASTOS_GUARDERIA_MENSUAL=MM:N[;MM-MM:N...]
                       (optional) the same spend month by month, sparse.
                       Entries are separated by ';' rather than ',' because
                       ',' already separates this flag's own keys, and a
                       month specification may be a range (9-12:210) for
                       the ordinary case of a constant fee across an
                       enrolment span. Required for the period in which the
                       child turns three, whose post-birthday months an
                       annual total cannot be apportioned across. Mutually
                       exclusive with GASTOS_GUARDERIA for one child.
NIF=XXXXXXXXX          (optional) NIF/NIE

Returns a validated DescendantInfo. Raises ValueError on missing required keys or invalid values.

Return type:

DescendantInfo

Parameters: