Source code for cadrumo.domain.contribuyente.meses_trabajo
"""The one grammar for the months a mother met the Art. 81.1 requirements.Art. 81.2 prorates its guardería increment by "el número de meses en que secumplan de forma simultánea los requisitos exigidos en el artículo 81.1 y 2",which is an intersection of two month SETS. A count cannot express it: a motherentitled May to August against nursery paid January to June overlaps in twomonths, and the manual works exactly that case to ``1.000 ÷ 12 × 2 = 166,67``while a count-based ``min(4, 6)`` yields four and 333,33 — an over-grant of thededucción, which under-declares tax.This module is why the months are now carried rather than counted. It shares themonth-specification grammar with :mod:`.guarderia_mensual` rather thanrestating it, so the two descendant month surfaces cannot drift into acceptingdifferent forms through different doors.Accepted form:: MM[;MM...] MM-MM (an inclusive month range)Ranges are INPUT only. :func:`serialise_meses_trabajo` always emits theexpanded, month-sorted ``MM`` form, so the stored fact has exactly onerepresentation and a save-then-reload round-trip is byte-stable regardless ofwhich form the operator typed.Every malformed input REFUSES rather than dropping the offending month. A monththat vanished between typing and storage would silently change the prorationbasis, and this set exists precisely to stop the basis being guessed."""from__future__importannotationsfromcollections.abcimportIterablefrom...core.errors.hierarchyimportProfileAnswerTypeErrorfrom.guarderia_mensualimportENTRY_SEPARATOR,parse_month_spec#: The accepted form, quoted verbatim in every refusal so the operator is told#: what to write rather than only what was wrong.MESES_TRABAJO_ACCEPTED_FORM="MM or MM-MM, entries separated by ';' (for example 5-8 or 1;2;11;12)"
[docs]defparse_meses_trabajo(raw:str,*,field:str)->tuple[int,...]:"""Parse the worked-months grammar into canonical, ascending months. Args: raw: The operator-supplied months. A blank string yields an empty tuple, which means "no qualifying months declared". field: The path or key naming this value in a refusal, so the operator is told which descendant and which door the bad value came through. Returns: The declared months, ascending, with every range expanded and no repeats. Raises: :class:`~cadrumo.core.errors.hierarchy.ProfileAnswerTypeError`: If any entry is malformed, names a month outside 1-12, inverts a range, or repeats a month. """ifnotraw.strip():return()months:set[int]=set()forchunkinraw.split(ENTRY_SEPARATOR):entry=chunk.strip()ifnotentry:# An empty entry is a stray or doubled separator, refused rather# than skipped for the same reason the spend grammar refuses one:# the other reading is a month whose text was lost.raiseProfileAnswerTypeError(f"{field} contains an empty entry; write {MESES_TRABAJO_ACCEPTED_FORM}.",)formonthinparse_month_spec(entry,entry=entry,field=field,accepted_form=MESES_TRABAJO_ACCEPTED_FORM):ifmonthinmonths:raiseProfileAnswerTypeError(f"{field} declares month {month} more than once. A month either qualified ""or it did not, so a repeat is a transcription slip rather than a figure ""to combine.",)months.add(month)returntuple(sorted(months))
[docs]defserialise_meses_trabajo(months:Iterable[int])->str:"""Render months in the one canonical form, ascending. Ranges are never emitted, so the stored fact has exactly one representation for a given set and the fact index, the wizard's resume projection and the CLI payload round-trip byte-for-byte whichever form was typed. """returnENTRY_SEPARATOR.join(f"{month:02d}"formonthinsorted(months))