cadrumo.domain.contribuyente.descendant_record module

Validated descendant facts and core Art. 58 eligibility behavior.

class DescendantRecordFields(**data)[source]

Bases: BaseModel

Canonical field vocabulary shared by domain records and projections.

Parameters:

data (Any)

birth_date: date
relacion: DescendantRelacion
inscripcion_registro_civil_date: date | None
acogimiento_resolucion_date: date | None
death_date: date | None
discapacidad_grado: Literal[0, 33, 65] | None
convive_con_contribuyente: bool
dependencia_economica: bool | None
custodia_compartida: bool
rentas_anuales_euros: Decimal | None
presenta_declaracion_propia: bool
prorrata_minimo: bool | None
meses_madre_trabajo: tuple[int, ...]
alta_posterior_nacimiento_mes: CalendarMonth | None
segundo_ciclo_infantil_inicio_mes: CalendarMonth | None
gastos_guarderia_euros: int
gastos_guarderia_mensuales: tuple[GuarderiaMonthSpend, ...]
nif: str | None
class DescendantRecordBase(**data)[source]

Bases: DescendantRecordFields

Structured per-descendant data for Art. 58 mínimo-por-descendientes.

This record drives the mínimo-por-descendientes calculation (casilla 0513) and the bajo-3-años supplement (Art. 58.2). It is intentionally richer than RentaDescendantProfile, which models official-form rows.

Fields

birth_date

Required date of birth.

relacion

The legal relationship linking this descendant to the contribuyente (DescendantRelacion). Defaults to the registry-declared ordinary-descendant token, so an absent fact means an ordinary descendant. Art. 58.1 assimilates tutela and acogimiento for the tranches while Art. 58.2 names only “adopción o acogimiento, tanto preadoptivo como permanente” for the increase, so the two clauses need this distinction drawn rather than inferred.

inscripcion_registro_civil_date

The Art. 58.2 entry event for an ADOPTION: the date the adoption was inscribed in the Registro Civil, or — where inscription is not required — the date of the resolución judicial o administrativa. One anchor with two legal sources, not two meanings, which is why it is one field. Permitted only on an registry-declared adoption record.

The date is the INSCRIPTION in the Registro Civil, which is what Art. 58.2 counts from — not the adoption’s finalisation. The two can fall in different periods, so the distinction is load-bearing rather than terminological.

acogimiento_resolucion_date

The Art. 58.2 entry event for an acogimiento: the date of the FIRST ENTITLING resolución — preadoptivo or permanente, the two shapes the statute names. Permitted on those records and on an ADOPTADO record, because Art. 58.2’s three periods are a CAP measured from the first entitling event rather than a count the later adoption restarts: a fostered-then-adopted child holds both dates and gets three periods in total, not six. A temporal placement is not entitling and therefore has no truthful value for this field.

discapacidad_grado

0 = sin discapacidad, 33 = grado ≥ 33 % < 65 %, 65 = grado ≥ 65 %. A disabled descendant remains mínimo-eligible regardless of age.

convive_con_contribuyente

Whether the descendant cohabits with the taxpayer (Art. 58.1 condition). Stays a FACT about the household and is never overloaded to carry the dependency case; the two are separate fields precisely so a filer who does not cohabit is not forced to misstate cohabitation to reach the allowance they are entitled to.

dependencia_economica

Whether the taxpayer contributes to this descendant’s economic upkeep without cohabiting. None (the default) means UNSET, which never assimilates; only an explicit True does, and only when the filer declares no judicial anualidades. The authority states the entitled case in terms — a progenitor with no custody, not even shared, paying no anualidades, who nonetheless contributes economically. False is a real answer distinct from unset: it records that the question was put and declined.

custodia_compartida

Art. 61 LIRPF: when True, both progenitors share custody under a judicial or administrative arrangement. The mínimo-por-descendientes and the bajo-3-años supplement for this child are split 50 % between them (Art. 61 prorrata). Default False (sole custody / not applicable). Setting this flag on a non-cohabiting descendant has no additional effect because eligibility already fails.

rentas_anuales_euros

The descendant’s own annual rentas EXCLUDING exempt income, the figure Art. 58.1 LIRPF caps (“no tenga rentas anuales, excluidas las exentas, superiores a 8.000 euros”). None means the operator has not declared a figure, which does NOT exclude the descendant — see is_eligible_ordinary() for why absence is read as “no rentas to declare” rather than as a disqualification.

presenta_declaracion_propia

Whether the descendant files their own IRPF return. Art. 61 norma 2ª LIRPF withdraws the mínimo entirely when a descendant who generates the entitlement “presenten declaración por este Impuesto con rentas superiores a 1.800 euros” — so this flag alone does not exclude; it excludes only in combination with rentas_anuales_euros above the norma 2ª threshold. Default False.

prorrata_minimo

Explicit per-descendant answer to Art. 61 norma 1ª: does another contribuyente also hold the right to this descendant’s mínimo? True prorates, False claims the full amount, and None (the default) leaves the question unanswered so the caller may derive it from profile signals. An explicit value ALWAYS wins over both custodia_compartida and any derivation.

meses_madre_trabajo

WHICH calendar months (1-12, ascending, no repeats) the mother met the Art. 81.1 requirements for this child. Used by the deducción maternidad as a count — min(meses × 100, 1_200) per eligible child — and by the Art. 81.2 guardería increment as a SET.

The months rather than their number, because Art. 81.2 prorates by “el número de meses en que se cumplan de forma simultánea los requisitos exigidos en el artículo 81.1 y 2”: an INTERSECTION with the declared nursery months, which a count cannot express. A mother entitled May to August against nursery paid January to June shares two months and the manual works 1.000 ÷ 12 × 2 = 166,67, where a count-based min(4, 6) yields four and 333,33 — an over-grant of the deducción, which under-declares tax. Default () (no deducción contribution from this child).

alta_posterior_nacimiento_mes

The calendar month (1-12) in which the mother — not registered with the Seguridad Social or a mutualidad at this child’s birth — completed the 30-day minimum contribution period Art. 81.1 LIRPF requires for the post-birth alta route (“que en dicho momento o en cualquier momento posterior estén dadas de alta … con un período mínimo, en este último caso, de 30 días cotizados”). None (the default) means the ordinary case: no post-birth alta increment applies, whether because the mother was already registered at the birth or because none is declared. This is the mother’s employment history, exactly as meses_madre_trabajo is, and this application does not hold it and must not infer it.

The route itself is filing-year gated: LIRPF art. 81.1 reached only a mother already registered “en el momento del nacimiento” before filing year 2023 (the lirpf-art-81-maternity-post-birth-enrollment-effective-year governed fact), so a declared month for an earlier filing year contributes no increment — see maternidad_alta_posterior_increment_applies().

gastos_guarderia_euros

Actual guardería / centro educación infantil autorizado expenses paid for this child (Art. 81.2 LIRPF), as an ANNUAL total. Integer euros, ≥ 0. Default 0 (no guardería expenses declared for this child). Sufficient only while the child is under three for the whole period; it cannot express either month boundary the statute draws.

gastos_guarderia_mensuales

The same spend broken down by month, sparse: only months with spend appear. Required for the period in which the child turns three, because that period needs the post-birthday months separated and an annual total cannot be apportioned across a birthday. Mutually exclusive with gastos_guarderia_euros for one child — declaring both is refused rather than reconciled, so a descendant always has exactly one spend authority.

death_date

The date this descendant died, or None (the default) for a descendant who did not die. Art. 61 norma 4ª LIRPF turns on this fact twice, and the two limbs read it differently, which is why one date drives both rather than a bare “died this year” flag.

A death IN the filing period replaces this descendant’s Art. 58.1 birth-order tranche with the norma 4ª flat cuantía, at any birth order — the flat figure coincides with the first-child tranche, so the substitution is invisible for a first child and worth 300 € to 2.100 € for a later one. The Art. 58.2 menor-3 supplement is NOT replaced: the AEAT manual states it “resulta aplicable en los casos en que el descendiente haya fallecido durante el período impositivo”, so it is added on top of the flat figure exactly as it is on top of a tranche.

A death BEFORE the devengo (31 December) additionally drops this descendant from the age ordering that ranks the survivors — the manual’s “sin computar a estos efectos aquellos descendientes que, en su caso, hubieran fallecido en el ejercicio con anterioridad a la fecha de devengo del impuesto”. The two limbs are deliberately NOT collapsed into one condition: the flat cuantía is owed on any death in the period, while the ordering exclusion is expressly conditioned on the death preceding the devengo, so a descendant who dies ON 31 December takes the flat figure and still occupies their rank. That case is degenerate but the clauses are worded differently, and reading them as one would be a choice neither text supports.

A death in a PRIOR year makes this descendant no part of this period at all — see meets_non_income_conditions().

nif

Optional NIF/NIE; validated for shape when present.

died_in_period(filing_year)[source]

True when this descendant died during filing_year.

The Art. 61 norma 4ª trigger for the FLAT cuantía, which the statute conditions on the fallecimiento alone with no reference to the devengo.

Return type:

bool

Parameters:

filing_year (int)

died_before_devengo(filing_year)[source]

True when this descendant died in filing_year before its 31 December devengo.

The trigger for the ORDERING limb only, which the AEAT manual conditions on the death falling “con anterioridad a la fecha de devengo del impuesto”. Strictly narrower than died_in_period(), and the gap between them is load-bearing rather than an oversight — see the death_date field documentation.

Return type:

bool

Parameters:

filing_year (int)

age_at_year_end(filing_year)[source]

Return the descendant’s age at the end of filing_year, or at death.

A descendant who died during the period is aged at their DEATH DATE, not at 31 December, and that distinction is load-bearing rather than pedantic. Aging them to year-end returns an age they never reached whenever their birthday falls between the death and 31 December, and the consequences are both silent and both against a bereaved filer: a child who died at 24 reads as 25 and fails the Art. 58.1 age limb outright, so the flat cuantía Art. 61 norma 4ª exists to grant is never reached; a toddler who died at two reads as three and loses the menor-tres increment this module’s own aggregate promises them.

Norma 4ª exists BECAUSE the deceased is absent at the devengo, so applying a 31-December test to someone the statute already treats as dead defeats the provision’s own premise. The AEAT manual cited at minimo_descendientes_estatal() grants the menor-tres increase to a descendant who died during the period, which is the same reading.

A death in a PRIOR year is not aged here at all – that descendant is already excluded by meets_non_income_conditions(), which fails on death_date.year < filing_year before any age question is asked.

Return type:

int

Parameters:

filing_year (int)

art_58_2_entry_date()[source]

The FIRST entitling entry event for the Art. 58.2 window, or None.

Art. 58.2’s three periods are a cap rather than a restart: where the circumstances change — the authority’s own worked example is an adoption following a fostering — the increase continues for the remaining periods up to a maximum of three. Anchoring on whichever event the record happens to hold would grant a fostered-then-adopted child up to six periods where the statute allows three, so the earliest entitling event is the anchor and the later one changes nothing.

Returns None for a relación the statute excludes from the limb (tutela, temporal acogimiento, an ordinary descendant) and for an entitling relación whose date is not yet recorded.

Return type:

date | None

exceeds_rentas_cap(thresholds)[source]

True when a DECLARED rentas figure breaches the Art. 58.1 ceiling.

Art. 58.1 LIRPF conditions the mínimo on the descendant holding no “rentas anuales, excluidas las exentas, superiores a” the ceiling, so the test is strictly greater-than: a descendant exactly AT the ceiling keeps the mínimo.

An undeclared figure (rentas_anuales_euros is None) returns False — absence of data is not evidence of income. The alternative default would zero the mínimo for every descendant nobody has yet entered a figure for, which is the overwhelming majority (a young child has no rentas to declare) and would be a large silent UNDER-claim against the taxpayer. The residual over-claim risk — a descendant who really does earn above the ceiling whose figure was never entered — is the operator’s to close by declaring the figure.

Return type:

bool

Parameters:

thresholds (MinimoDescendientesThresholds)

excluded_by_declaracion_propia(thresholds)[source]

True when Art. 61 norma 2ª withdraws the mínimo for this descendant.

Norma 2ª bars the mínimo when the descendant who generates the entitlement “presenten declaración por este Impuesto con rentas superiores a” the norma 2ª figure. Both halves are required: filing a return is not disqualifying on its own (the AEAT manual is explicit that a descendant filing with “rentas iguales o inferiores a 1.800 euros” leaves the mínimo intact), and rentas alone are governed by the separate Art. 58.1 ceiling.

An undeclared rentas figure returns False for the same reason as exceeds_rentas_cap().

Return type:

bool

Parameters:

thresholds (MinimoDescendientesThresholds)

is_eligible_ordinary(filing_year, *, thresholds, context, dependencia_assimilation_available=False)[source]

True when the descendant qualifies for the Art. 58.1 ordinary mínimo.

Eligibility requires ALL of:

thresholds is a required keyword argument rather than an optional one so no caller can silently evaluate the age/cohabitation half of the law while skipping the two income conditions.

Return type:

bool

Parameters:
meets_non_income_conditions(filing_year, *, context, dependencia_assimilation_available=False)[source]

The half of Art. 58.1 that needs no ceiling: the household limb and age/discapacidad.

Split out rather than inlined because one caller genuinely cannot supply the ceilings and does not need them. A descendant with NO declared rentas figure can never be excluded by either income condition — both read an absent figure as non-excluding — so for that descendant eligibility reduces exactly to this predicate. The calculate-path advisory that flags those descendants therefore asks this question directly instead of re-deriving it beside is_eligible_ordinary(), which is how the two would drift.

A descendant who died in a year BEFORE filing_year fails outright, ahead of every other condition. They were no part of this period, and the age limb would otherwise keep answering for them indefinitely — birth_date alone goes on satisfying “under 25 at year-end” for years after the death, so without this gate a bereaved filer would keep claiming the mínimo for a child who died long ago, and the norma 4ª flat cuantía would not apply either because that limb is scoped to a death IN the period. Gated here rather than in is_eligible_ordinary() so the calculate-path advisory that asks this predicate directly cannot answer differently from the aggregate.

The household limb is cohabitation OR assimilated economic dependency. The authority states the dependency case in terms: a progenitor without custody, not even shared, and paying no judicial anualidades, who nonetheless contributes to the descendant’s economic upkeep “tendrá derecho a la aplicación del mínimo por descendientes”. Cohabitation is therefore sufficient but not necessary, which is what an earlier reading of this predicate got wrong.

Assimilation requires BOTH halves and neither is a default. The descendant must carry an explicit dependencia_economica = True — an unset field never assimilates, because the reachable-through-the-only- available-field shape is how an excluded case gets granted. And the caller must pass dependencia_assimilation_available, which the profile computes from the filer-level anualidades declaration.

That flag defaults to False, so a caller that forgets it gets the UNDER-granting answer rather than the claiming one. The default is the safe direction by construction rather than by convention.

Not a public substitute for is_eligible_ordinary(). Answering True here says only that the non-income conditions hold; a caller that HAS a rentas figure must still apply the ceilings.

Return type:

bool

Parameters:
qualifies_on_household_limb(*, dependencia_assimilation_available=False)[source]

True when cohabitation holds, or economic dependency is assimilated in its place.

Named and public because three surfaces ask this question and one of them — the advisory that discloses the assimilation to the operator — needs it apart from the age limb.

Return type:

bool

Parameters:

dependencia_assimilation_available (bool)

is_eligible_menor_tres(filing_year, *, context)[source]

True when the descendant is under three at the devengo date and cohabits.

Scoped to the Art. 81 deductions — the deducción por maternidad (art. 81.1) and the guardería incremento (art. 81.2). It is NOT the Art. 58.2 test: see is_eligible_minimo_incremento_menor_tres(), which carries an additional limb this one deliberately lacks.

Two known narrowings against the Art. 81 text, both in the over-taxing direction and both needing data this axis does not carry: art. 81.1 runs monthly “hasta que el menor alcance los tres años de edad” rather than testing age once at year-end, and art. 81.2 extends the guardería incremento through the period in which the child turns three, “respecto de los gastos incurridos con posterioridad al cumplimiento de dicha edad hasta el mes anterior a aquel en el que pueda comenzar el segundo ciclo de educación infantil”. Both need month-level figures; gastos_guarderia_euros is an annual total, so widening this predicate alone would swap an under-grant for an over-grant.

Return type:

bool

Parameters:
Parameters:
  • birth_date (date)

  • relacion (DescendantRelacion)

  • inscripcion_registro_civil_date (date | None)

  • acogimiento_resolucion_date (date | None)

  • death_date (date | None)

  • discapacidad_grado (Literal[0, 33, 65] | None)

  • convive_con_contribuyente (bool)

  • dependencia_economica (bool | None)

  • custodia_compartida (bool)

  • rentas_anuales_euros (Annotated[Decimal | None, Ge(ge=Decimal('0'))])

  • presenta_declaracion_propia (bool)

  • prorrata_minimo (bool | None)

  • meses_madre_trabajo (tuple[int, ...])

  • alta_posterior_nacimiento_mes (Annotated[int, FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=1), Le(le=12)])] | None)

  • segundo_ciclo_infantil_inicio_mes (Annotated[int, FieldInfo(annotation=NoneType, required=True, metadata=[Ge(ge=1), Le(le=12)])] | None)

  • gastos_guarderia_euros (Annotated[int, Ge(ge=0)])

  • gastos_guarderia_mensuales (tuple[GuarderiaMonthSpend, ...])

  • nif (str | None)