Second-level bargaining¶
Company or territorial agreements may add allowances on top of the national
CCNL minimum. Second-level amounts are not an input of the engine: there is no
model for a caller-supplied second-level allowance, and a CCNL data file
carries only the allowances of the national agreement. An amount agreed at
company level that must be paid can be declared today only through the work
events of the run, for example a BonusEvent, with the treatment of that
event.
See Domain: Second-level bargaining for the legal background.
Company agreements and the calendar¶
A company agreement can change when an extra month is paid, or grant more
than the CCNL. The year calculation accepts such a calendar only as a
CalendarOverride with its reason, validated against the CCNL calendar:
"""Full-year run: standard calendar derived from the CCNL, then an override.
Without ``calendar_override`` the engine runs the calendar the CCNL grants. A custom
calendar is accepted only as a ``CalendarOverride`` with a domain reason; an
override that drops or lowers a CCNL extra month raises ``InvalidInputError``.
"""
from ccnl_engine import (
CompetenceYearPlan,
EmployerProfile,
Employment,
Headcount,
InvalidInputError,
PayrollEngine,
)
from ccnl_engine.inputs import CalendarOverride, CalendarOverrideReason, WorkCalendar
from ccnl_engine.inputs import Permanent
engine = PayrollEngine.bundled()
def year_request(calendar: CalendarOverride | None = None) -> CompetenceYearPlan:
"""Build a Commercio level 4 input for 2026.
Returns:
The year input, with ``calendar`` as the optional override.
"""
return CompetenceYearPlan(
year=2026,
employment=Employment(
ccnl_slug="commercio-confcommercio.json",
level_code="4",
contract_type=Permanent(),
),
employer=EmployerProfile(headcount=Headcount(50)),
calendar_override=calendar,
)
# Standard calendar: tredicesima in December, quattordicesima in June.
result = engine.calculate_competence_year(year_request())
print(f"Annual gross: {result.annual_gross}")
print(f"Annual net: {result.annual_net}")
print(f"Annual employer cost:{result.annual_employer_cost}")
print(f"Runs computed: {len(result.period_results)}")
for r in result.period_results:
run_label = r.run.run_id if r.run else str(r.period_id)
print(f" {run_label:30s} gross={r.period_gross}")
# Same entitlement, quattordicesima paid with the July salary.
july = CalendarOverride(
calendar=WorkCalendar.from_additional_months(2026, 14, fourteenth_payment_month=7),
reason=CalendarOverrideReason.PAYMENT_MONTH,
note="quattordicesima paid with the July salary",
)
moved = engine.calculate_competence_year(year_request(july))
print(f"Runs with July quattordicesima: {len(moved.period_results)}")
# Dropping the quattordicesima is rejected whatever the reason.
only_thirteenth = CalendarOverride(
calendar=WorkCalendar.from_additional_months(2026, 13),
reason=CalendarOverrideReason.PAYMENT_MONTH,
note="attempt to skip the quattordicesima",
)
try:
engine.calculate_competence_year(year_request(only_thirteenth))
except InvalidInputError as exc:
print(f"Rejected: {exc}")
Note
A premio di risultato paid under a second-level agreement is a
BonusEvent with kind="productivity_bonus". Its 1% substitute tax
needs the prior-year employment income in PriorYearTaxFacts; without
it the bonus is taxed at ordinary rates.
API reference: CompetenceYearPlan, CalendarOverride