ccnl-engine 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. ccnl_engine-0.1.0/LICENSE +21 -0
  2. ccnl_engine-0.1.0/PKG-INFO +181 -0
  3. ccnl_engine-0.1.0/README.md +158 -0
  4. ccnl_engine-0.1.0/pyproject.toml +134 -0
  5. ccnl_engine-0.1.0/setup.cfg +4 -0
  6. ccnl_engine-0.1.0/src/ccnl_engine/__init__.py +1 -0
  7. ccnl_engine-0.1.0/src/ccnl_engine/contracts/__init__.py +1 -0
  8. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/__init__.py +1 -0
  9. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/acconciatura-estetica-confartigianato.json +412 -0
  10. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/agenzie-viaggio-fiavet.json +618 -0
  11. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/alimentari-federalimentare.json +866 -0
  12. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/autoferrotranvieri-internavigatori.json +2095 -0
  13. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/bancari-abi.json +468 -0
  14. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/bcc-credito-cooperativo.json +493 -0
  15. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/calzaturiero-assocalzaturifici.json +505 -0
  16. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/carta-cartone-assocarta.json +751 -0
  17. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/ceramica-industria-confindustria.json +739 -0
  18. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/chimica-affini-pmi-unionchimica.json +390 -0
  19. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/chimica-farmaceutica-federchimica.json +713 -0
  20. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/commercio-confcommercio.json +723 -0
  21. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/comunicazione-artigianato-confartigianato.json +525 -0
  22. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/cooperative-sociali.json +748 -0
  23. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/dirigenza-funzioni-centrali-aran.json +126 -0
  24. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/dirigenza-funzioni-locali-aran.json +115 -0
  25. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/dirigenza-istruzione-ricerca-aran.json +136 -0
  26. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/dirigenza-sanitaria-area-sanita-aran.json +141 -0
  27. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/dirigenza-sanitaria-medico-veterinaria-aran.json +144 -0
  28. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/dmo-federdistribuzione.json +749 -0
  29. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/edilizia-ance.json +374 -0
  30. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/edilizia-artigianato-cna.json +867 -0
  31. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/elettrico-elettricita-futura.json +910 -0
  32. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/funzioni-centrali-aran.json +168 -0
  33. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/funzioni-locali-aran.json +189 -0
  34. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/gas-acqua-utilitalia.json +713 -0
  35. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/gomma-plastica-federazione-gomma-plastica.json +669 -0
  36. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/grafica-editoria-aieg.json +705 -0
  37. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/igiene-ambientale-utilitalia.json +1316 -0
  38. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/impiegati-tecnici-agricoli.json +282 -0
  39. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/istruzione-ricerca-aran.json +222 -0
  40. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/lavoro-domestico-convivente.json +309 -0
  41. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/lavoro-domestico-non-convivente.json +280 -0
  42. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/legno-arredamento-federlegno.json +1266 -0
  43. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/legno-lapidei-artigianato-confartigianato.json +587 -0
  44. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/logistica-trasporto-confetra.json +473 -0
  45. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/metalmeccanico-artigianato.json +689 -0
  46. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/metalmeccanico-confapi.json +471 -0
  47. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/metalmeccanico-federmeccanica.json +625 -0
  48. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/multiservizi-anip.json +1461 -0
  49. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/operai-agricoli-florovivaisti.json +206 -0
  50. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/orafi-argentieri-industria-federorafi.json +460 -0
  51. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/panificazione-artigianato-confartigianato.json +729 -0
  52. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/panificazione-assipan.json +312 -0
  53. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/pelli-cuoio-industria-assopellettieri.json +486 -0
  54. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/pubblici-esercizi-fipe-angem.json +585 -0
  55. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/sanita-aran.json +192 -0
  56. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/sanita-privata-aiop-aris.json +534 -0
  57. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/studi-professionali-confprofessioni.json +466 -0
  58. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/telecomunicazioni-asstel.json +488 -0
  59. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/terziario-confesercenti.json +568 -0
  60. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/tessile-moda-artigianato-confartigianato.json +505 -0
  61. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/tessile-smi.json +646 -0
  62. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/trasporto-aereo-assaeroporti.json +946 -0
  63. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/trasporto-ferroviario-agens.json +695 -0
  64. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/turismo-confcommercio.json +580 -0
  65. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/turismo-federalberghi.json +628 -0
  66. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/uneba-uneba.json +571 -0
  67. ccnl_engine-0.1.0/src/ccnl_engine/contracts/data/vigilanza-privata-assiv.json +459 -0
  68. ccnl_engine-0.1.0/src/ccnl_engine/contracts/loaders.py +22 -0
  69. ccnl_engine-0.1.0/src/ccnl_engine/engine/__init__.py +1 -0
  70. ccnl_engine-0.1.0/src/ccnl_engine/engine/compute.py +583 -0
  71. ccnl_engine-0.1.0/src/ccnl_engine/engine/contributions.py +122 -0
  72. ccnl_engine-0.1.0/src/ccnl_engine/engine/irpef.py +80 -0
  73. ccnl_engine-0.1.0/src/ccnl_engine/engine/result.py +57 -0
  74. ccnl_engine-0.1.0/src/ccnl_engine/engine/rounding.py +14 -0
  75. ccnl_engine-0.1.0/src/ccnl_engine/models/__init__.py +1 -0
  76. ccnl_engine-0.1.0/src/ccnl_engine/models/apprenticeship.py +152 -0
  77. ccnl_engine-0.1.0/src/ccnl_engine/models/ccnl.py +492 -0
  78. ccnl_engine-0.1.0/src/ccnl_engine/models/employment.py +42 -0
  79. ccnl_engine-0.1.0/src/ccnl_engine/models/validity.py +79 -0
  80. ccnl_engine-0.1.0/src/ccnl_engine/py.typed +0 -0
  81. ccnl_engine-0.1.0/src/ccnl_engine/tax/__init__.py +1 -0
  82. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-agricoltura.json +61 -0
  83. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-artigianato.json +82 -0
  84. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-credito.json +77 -0
  85. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-edilizia.json +92 -0
  86. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-industria.json +91 -0
  87. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-lavoro-domestico.json +54 -0
  88. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-pubblica-amministrazione.json +75 -0
  89. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/2026-terziario.json +87 -0
  90. ccnl_engine-0.1.0/src/ccnl_engine/tax/data/__init__.py +1 -0
  91. ccnl_engine-0.1.0/src/ccnl_engine/tax/loaders.py +163 -0
  92. ccnl_engine-0.1.0/src/ccnl_engine/tax/models.py +481 -0
  93. ccnl_engine-0.1.0/src/ccnl_engine.egg-info/PKG-INFO +181 -0
  94. ccnl_engine-0.1.0/src/ccnl_engine.egg-info/SOURCES.txt +95 -0
  95. ccnl_engine-0.1.0/src/ccnl_engine.egg-info/dependency_links.txt +1 -0
  96. ccnl_engine-0.1.0/src/ccnl_engine.egg-info/requires.txt +1 -0
  97. ccnl_engine-0.1.0/src/ccnl_engine.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lucas Puerari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: ccnl-engine
3
+ Version: 0.1.0
4
+ Summary: Italian CCNL payroll engine: gross-to-net and employer cost from first principles
5
+ Author-email: Intella <dev@intella.tech>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/lucas-puerari/ccnl-engine
8
+ Project-URL: Repository, https://github.com/lucas-puerari/ccnl-engine
9
+ Project-URL: Bug Tracker, https://github.com/lucas-puerari/ccnl-engine/issues
10
+ Keywords: ccnl,payroll,italy,labor,hrm
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Office/Business :: Financial
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pydantic>=2.0
22
+ Dynamic: license-file
23
+
24
+ # ccnl-engine
25
+
26
+ [![PyPI version](https://img.shields.io/pypi/v/ccnl-engine?logo=pypi&logoColor=white)](https://pypi.org/project/ccnl-engine/)
27
+ [![Python](https://img.shields.io/pypi/pyversions/ccnl-engine?logo=python&logoColor=white)](https://pypi.org/project/ccnl-engine/)
28
+ [![CI](https://github.com/lucas-puerari/ccnl-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/lucas-puerari/ccnl-engine/actions/workflows/ci.yml)
29
+ [![Coverage](https://codecov.io/gh/lucas-puerari/ccnl-engine/graph/badge.svg)](https://codecov.io/gh/lucas-puerari/ccnl-engine)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
31
+
32
+ A Python library for modeling Italian collective labor agreements (CCNL) as structured, versioned data and computing gross-to-net salary and employer cost from first principles.
33
+
34
+ ## Why
35
+
36
+ Italian payroll is governed by collective agreements (CCNL) that define base salaries, seniority increments, and allowances as time-series values — they change at negotiated renewal dates. Existing tools either lock this data inside proprietary systems or require a full HRMS. This library treats each CCNL as a validated JSON file and the computation as a pure function:
37
+
38
+ ```
39
+ compute(ccnl, rules, ComputeRequest(...)) → ComputationResult
40
+ ```
41
+
42
+ ## Quickstart
43
+
44
+ ```python
45
+ from datetime import date
46
+ from ccnl_engine.contracts.loaders import load_ccnl
47
+ from ccnl_engine.tax.loaders import load_year_rules
48
+ from ccnl_engine.engine.compute import ComputeRequest, compute
49
+ from ccnl_engine.models.ccnl import TaxSector
50
+ from ccnl_engine.models.employment import Permanent
51
+
52
+ ccnl = load_ccnl("commercio-confcommercio.json")
53
+ rules = load_year_rules(2026, TaxSector.TERZIARIO, num_employees=50)
54
+ result = compute(
55
+ ccnl,
56
+ rules,
57
+ ComputeRequest(level_code="4", as_of=date(2026, 9, 1), employment=Permanent()),
58
+ )
59
+
60
+ print(result.net_annual) # → Decimal('...')
61
+ print(result.employer_cost_annual) # → Decimal('...')
62
+ ```
63
+
64
+ ## CCNL coverage
65
+
66
+ *Layers legend*
67
+
68
+ | Layer | Meaning |
69
+ | ----- | ------- |
70
+ | **Layer 1** | base salary, seniority increments (*scatti di anzianità*), fixed allowances, additional months. |
71
+ | **Layer 2** | part-time, fixed-term (NASpI *addizionale*), apprenticeship (percentage or under-classification). |
72
+
73
+ *Status legend*
74
+
75
+ | Symbol | Meaning |
76
+ |--------|---------|
77
+ | ✅ | Fully modelled; all salary tables and rules for this layer are in scope |
78
+ | ⚠️ | Partially modelled; known gaps or simplifications apply — see `coverage.notes` in the contract's JSON data file |
79
+ | 🤖 | Extraction: extracted automatically via Claude Code — no manual human review |
80
+ | 🧑 | Extraction: verified manually against the official source |
81
+
82
+ *CCNL Matrix*
83
+
84
+ | # | CCNL | Sector | Lavoratori (~)<sup><a id="ref-1" href="#fn-1">1</a></sup> | Layer 1 | Layer 2 | Extraction<sup><a id="ref-2" href="#fn-2">2</a></sup> |
85
+ |---|---|---|---:|:---:|:---:|:---:|
86
+ | 1 | Commercio — Confcommercio | Terziario | ~800k | ✅ | ✅ | 🤖 |
87
+ | 2 | Metalmeccanico — Federmeccanica/Assistal | Industria | ~1,7M | ✅ | ✅ | 🤖 |
88
+ | 3 | Metalmeccanico PMI — Unionmeccanica-Confapi | Industria | ~350k | ✅ | ✅ | 🤖 |
89
+ | 4 | Chimica-Farmaceutica — Federchimica/Farmindustria/Assistal | Industria | ~210k | ✅ | ✅ | 🤖 |
90
+ | 5 | Turismo — Confcommercio | Terziario | ~300k | ✅ | ✅ | 🤖 |
91
+ | 6 | Edilizia — ANCE | Edilizia | ~550k | ✅ | ✅ | 🤖 |
92
+ | 7 | Cooperative Sociali — Confcooperative/Legacoop/AGCI | Terziario | ~380k | ✅ | ✅ | 🤖 |
93
+ | 8 | Logistica, Trasporto Merci e Spedizione — Confetra | Industria | ~430k | ✅ | ✅ | 🤖 |
94
+ | 9 | Servizi di Pulizia e Multiservizi — ANIP-Confindustria | Terziario | ~580k | ✅ | ✅ | 🤖 |
95
+ | 10 | Studi e Attività Professionali — Confprofessioni | Terziario | ~350k | ✅ | ✅ | 🤖 |
96
+ | 11 | Credito — ABI | Credito | ~270k | ✅ | ✅ | 🤖 |
97
+ | 12 | Tessile Abbigliamento Moda — SMI | Industria | ~160k | ✅ | ✅ | 🤖 |
98
+ | 13 | Alimentari Industria — Federalimentare | Industria | ~145k | ✅ | ✅ | 🤖 |
99
+ | 14 | Distribuzione Moderna Organizzata — Federdistribuzione | Terziario | ~460k | ✅ | ✅ | 🤖 |
100
+ | 15 | Metalmeccanica e Installazione Impianti — Artigianato | Artigianato | ~350k | ✅ | ✅ | 🤖 |
101
+ | 16 | Gomma e Plastica Industria — Federazione Gomma Plastica | Industria | ~90k | ✅ | ✅ | 🤖 |
102
+ | 17 | Grafica e Editoria — AIEG-Acigraf | Industria | ~70k | ✅ | ✅ | 🤖 |
103
+ | 18 | Carta e Cartone — Assocarta | Industria | ~35k | ✅ | ✅ | 🤖 |
104
+ | 19 | Telecomunicazioni — Asstel | Industria | ~110k | ✅ | ✅ | 🤖 |
105
+ | 20 | Vigilanza Privata — ASSIV/ANIVP/UNIV (GPG) | Terziario | ~85k | ✅ | ✅ | 🤖 |
106
+ | 21 | Legno e Arredamento — Federlegno-Arredo | Industria | ~90k | ✅ | ✅ | 🤖 |
107
+ | 22 | Edilizia e Affini — CNA/Confartigianato/Casartigiani | Artigianato | ~350k | ✅ | ✅ | 🤖 |
108
+ | 23 | Gas e Acqua — Utilitalia/Proxigas/Anfida/Assogas | Industria | ~65k | ✅ | ✅ | 🤖 |
109
+ | 24 | Istituzioni Socio-Assistenziali — UNEBA | Terziario | ~130k | ✅ | ✅ | 🤖 |
110
+ | 25 | Acconciatura ed Estetica — Confartigianato/CNA | Artigianato | ~95k | ✅ | ✅ | 🤖 |
111
+ | 26 | Area Alimentazione e Panificazione — Artigianato (Confartigianato/CNA) | Artigianato | ~90k | ✅ | ✅ | 🤖 |
112
+ | 27 | Autoferrotranvieri e Internavigatori (Mobilita/TPL) — AGENS/ASSTRA/ANAV | Terziario | ~120k | ✅ | ✅ | 🤖 |
113
+ | 28 | Credito Cooperativo (BCC/CRA) — Federcasse | Credito | ~33k | ✅ | ✅ | 🤖 |
114
+ | 29 | Elettrico (produzione/distribuzione energia) — Elettricita Futura | Industria | ~60k | ✅ | ✅ | 🤖 |
115
+ | 30 | Calzaturiero (industria delle calzature) — Assocalzaturifici | Industria | ~75k | ✅ | ✅ | 🤖 |
116
+ | 31 | Area Tessile-Moda e Chimica-Ceramica — Artigianato (Confartigianato/CNA) | Artigianato | ~120k | ✅ | ✅ | 🤖 |
117
+ | 32 | Area Legno-Lapidei — Artigianato (Confartigianato/CNA) | Artigianato | ~95k | ✅ | ✅ | 🤖 |
118
+ | 33 | Area Comunicazione — Artigianato (Confartigianato/CNA) | Artigianato | ~60k | ✅ | ✅ | 🤖 |
119
+ | 34 | Ceramica Industria — Confindustria Ceramica (Assopiastrelle) | Industria | ~23k | ✅ | ✅ | 🤖 |
120
+ | 35 | Orafi e Argentieri — Federorafi | Industria | ~18k | ✅ | ✅ | 🤖 |
121
+ | 36 | Pelli e Cuoio Industria — Assopellettieri | Industria | ~17k | ✅ | ✅ | 🤖 |
122
+ | 37 | Pubblici Esercizi, Ristorazione Collettiva e Turismo — FIPE/ANGEM | Terziario | ~350k | ✅ | ✅ | 🤖 |
123
+ | 38 | Agenzie di Viaggio e Turismo — Fiavet/Confcommercio | Terziario | ~25k | ✅ | ✅ | 🤖 |
124
+ | 39 | Terziario Distribuzione e Servizi — Confesercenti | Terziario | ~230k | ✅ | ✅ | 🤖 |
125
+ | 40 | Turismo — Federalberghi/Faita | Terziario | ~220k | ✅ | ✅ | 🤖 |
126
+ | 41 | Funzioni Centrali 2022-2024 — ARAN (Ministeri, Agenzie, INPS, INAIL) | Pubblica Amministrazione | ~250k | ✅ | ✅ | 🤖 |
127
+ | 42 | Funzioni Locali 2022-2024 — ARAN (Comuni, Province, Regioni, Camere di Commercio) | Pubblica Amministrazione | ~400k | ✅ | ✅ | 🤖 |
128
+ | 43 | Comparto Sanità 2022-2024 — ARAN (SSN non-dirigenza) | Pubblica Amministrazione | ~580k | ✅ | ✅ | 🤖 |
129
+ | 44 | Area Sanità 2022-2024 — ARAN (Dirigenti Medici e Veterinari SSN) | Pubblica Amministrazione | ~100k | ✅ | ✅ | 🤖 |
130
+ | 45 | Area Sanità 2022-2024 — ARAN (Dirigenti Sanitari: psicologi, farmacisti, biologi) | Pubblica Amministrazione | ~37k | ✅ | ✅ | 🤖 |
131
+ | 46 | Area Dirigenza Funzioni Locali 2022-2024 — ARAN (Dirigenti enti locali, Segretari comunali) | Pubblica Amministrazione | ~13k | ✅ | ✅ | 🤖 |
132
+ | 47 | Area Dirigenza Funzioni Centrali 2022-2024 — ARAN (Dirigenti ministeri, agenzie fiscali, INPS, INAIL) | Pubblica Amministrazione | ~30k | ✅ | ✅ | 🤖 |
133
+ | 48 | Area Dirigenza Istruzione e Ricerca 2022-2024 — ARAN (Dirigenti scolastici, universitari, ricerca) | Pubblica Amministrazione | ~8k | ✅ | ✅ | 🤖 |
134
+ | 49 | Comparto Istruzione e Ricerca 2022-2024 — ARAN (Docenti e ATA scuola, università, ricerca) | Pubblica Amministrazione | ~1,2M | ✅ | ✅ | 🤖 |
135
+ | 50 | Case di Cura Private - Personale Non Medico (AIOP/ARIS) | Sanità privata | ~150k | ✅ | ✅ | 🤖 |
136
+ | 51 | Lavoro Domestico — DOMINA/FIDALDO/ASSINDATCOLF (conviventi) | Lavoro Domestico | ~900k | ✅ | ✅ | 🤖 |
137
+ | 52 | Lavoro Domestico — DOMINA/FIDALDO/ASSINDATCOLF (non conviventi) | Lavoro Domestico | ~900k | ✅ | ✅ | 🤖 |
138
+ | 53 | Operai Agricoli e Florovivaisti — Coldiretti/Confagricoltura/CIA | Agricoltura | ~600k | ✅ | ✅ | 🤖 |
139
+ | 54 | Chimica e Affini PMI — Unionchimica Confapi | Chimica | ~56k | ✅ | ✅ | 🤖 |
140
+ | 55 | Panificazione e Settori Affini Industria — Assipan/Fiesa/Federpanificatori | Alimentare | ~20k | ✅ | ✅ | 🤖 |
141
+ | 56 | Attività Ferroviarie — AGENS | Trasporto | ~75k | ✅ | ✅ | 🤖 |
142
+ | 57 | Trasporto Aereo — Gestori Aeroportuali (Assaeroporti) | Trasporto | ~40k | ✅ | ✅ | 🤖 |
143
+ | 58 | Igiene Ambientale — Servizi Ambientali e di Igiene Urbana (Utilitalia/FISE) | Industria | ~65k | ✅ | ✅ | 🤖 |
144
+ | 59 | Impiegati e Tecnici Agricoli — Confagricoltura/CIA/Coldiretti | Agricoltura | ~80k | ✅ | ✅ | 🤖 |
145
+
146
+ <p id="fn-1"><a href="#ref-1">1.</a> Approximate estimates. Sources: CNEL, INPS, Ministero del Lavoro, CCNL renewal communications.</p>
147
+
148
+ <p id="fn-2"><a href="#ref-2">2.</a> Salary tables were extracted from official CCNL documents using Claude Code (AI-assisted), without manual human review. Values should be verified against the official source before use in production payroll systems.</p>
149
+
150
+ The 59 contracts above cover approximately **14.3 million workers** (sum of per-CCNL estimates; some overlap is possible where sector boundaries are not mutually exclusive, so the unique-worker count is somewhat lower). Italy has roughly **16 million employees** covered by some collective agreement (ISTAT/CNEL 2024, public and private sectors combined). This library therefore reaches an estimated **~85–90% of CCNL-covered workers** on a gross-headcount basis.
151
+
152
+ ## What is not modelled
153
+
154
+ - Addizionali regionali and addizionali comunali
155
+ - Detrazioni per carichi di famiglia (Art. 12 TUIR)
156
+ - IVS contributory ceiling split (Art. 1 L. 335/1995)
157
+ - Second-level bargaining (territorial and company agreements)
158
+ - Bilateral system contributions (EST, Fon.Te, …)
159
+ - Overtime, night/holiday premiums, leave accruals, sick-pay integrations
160
+
161
+ Each limitation is documented in the relevant data file's `coverage.notes` field and marked with `# SIMPLIFICATION:` comments in the engine source.
162
+
163
+ ## Releasing a new version
164
+
165
+ Releases are fully automated via GitHub Actions. Steps:
166
+
167
+ 1. Bump `version` in `pyproject.toml` following [SemVer](https://semver.org/).
168
+ 2. Commit the change: `git commit -m "chore: bump version to X.Y.Z"`.
169
+ 3. Push a tag: `git tag vX.Y.Z && git push origin vX.Y.Z`.
170
+
171
+ The `release` workflow then: runs the full test suite, builds the wheel and sdist with `uv build`, verifies the tag matches the package version, publishes to PyPI via [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no API token required), and creates a GitHub Release with auto-generated notes and the dist files attached.
172
+
173
+ **Pre-requisite (one-time):** configure a Trusted Publisher on PyPI for this repository under the `pypi` GitHub environment.
174
+
175
+ ## Development
176
+
177
+ This library was built with AI coding assistants — [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), and [Opencode](https://opencode.ai). Generated code is covered by the test suite but has not been manually reviewed line by line.
178
+
179
+ ## Disclaimer
180
+
181
+ This library is not legal or tax advice. Figures are computed from publicly available CCNL tables and statutory rates as of the dates indicated in the data files. Always verify results against official sources or a qualified payroll professional.
@@ -0,0 +1,158 @@
1
+ # ccnl-engine
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/ccnl-engine?logo=pypi&logoColor=white)](https://pypi.org/project/ccnl-engine/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/ccnl-engine?logo=python&logoColor=white)](https://pypi.org/project/ccnl-engine/)
5
+ [![CI](https://github.com/lucas-puerari/ccnl-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/lucas-puerari/ccnl-engine/actions/workflows/ci.yml)
6
+ [![Coverage](https://codecov.io/gh/lucas-puerari/ccnl-engine/graph/badge.svg)](https://codecov.io/gh/lucas-puerari/ccnl-engine)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+
9
+ A Python library for modeling Italian collective labor agreements (CCNL) as structured, versioned data and computing gross-to-net salary and employer cost from first principles.
10
+
11
+ ## Why
12
+
13
+ Italian payroll is governed by collective agreements (CCNL) that define base salaries, seniority increments, and allowances as time-series values — they change at negotiated renewal dates. Existing tools either lock this data inside proprietary systems or require a full HRMS. This library treats each CCNL as a validated JSON file and the computation as a pure function:
14
+
15
+ ```
16
+ compute(ccnl, rules, ComputeRequest(...)) → ComputationResult
17
+ ```
18
+
19
+ ## Quickstart
20
+
21
+ ```python
22
+ from datetime import date
23
+ from ccnl_engine.contracts.loaders import load_ccnl
24
+ from ccnl_engine.tax.loaders import load_year_rules
25
+ from ccnl_engine.engine.compute import ComputeRequest, compute
26
+ from ccnl_engine.models.ccnl import TaxSector
27
+ from ccnl_engine.models.employment import Permanent
28
+
29
+ ccnl = load_ccnl("commercio-confcommercio.json")
30
+ rules = load_year_rules(2026, TaxSector.TERZIARIO, num_employees=50)
31
+ result = compute(
32
+ ccnl,
33
+ rules,
34
+ ComputeRequest(level_code="4", as_of=date(2026, 9, 1), employment=Permanent()),
35
+ )
36
+
37
+ print(result.net_annual) # → Decimal('...')
38
+ print(result.employer_cost_annual) # → Decimal('...')
39
+ ```
40
+
41
+ ## CCNL coverage
42
+
43
+ *Layers legend*
44
+
45
+ | Layer | Meaning |
46
+ | ----- | ------- |
47
+ | **Layer 1** | base salary, seniority increments (*scatti di anzianità*), fixed allowances, additional months. |
48
+ | **Layer 2** | part-time, fixed-term (NASpI *addizionale*), apprenticeship (percentage or under-classification). |
49
+
50
+ *Status legend*
51
+
52
+ | Symbol | Meaning |
53
+ |--------|---------|
54
+ | ✅ | Fully modelled; all salary tables and rules for this layer are in scope |
55
+ | ⚠️ | Partially modelled; known gaps or simplifications apply — see `coverage.notes` in the contract's JSON data file |
56
+ | 🤖 | Extraction: extracted automatically via Claude Code — no manual human review |
57
+ | 🧑 | Extraction: verified manually against the official source |
58
+
59
+ *CCNL Matrix*
60
+
61
+ | # | CCNL | Sector | Lavoratori (~)<sup><a id="ref-1" href="#fn-1">1</a></sup> | Layer 1 | Layer 2 | Extraction<sup><a id="ref-2" href="#fn-2">2</a></sup> |
62
+ |---|---|---|---:|:---:|:---:|:---:|
63
+ | 1 | Commercio — Confcommercio | Terziario | ~800k | ✅ | ✅ | 🤖 |
64
+ | 2 | Metalmeccanico — Federmeccanica/Assistal | Industria | ~1,7M | ✅ | ✅ | 🤖 |
65
+ | 3 | Metalmeccanico PMI — Unionmeccanica-Confapi | Industria | ~350k | ✅ | ✅ | 🤖 |
66
+ | 4 | Chimica-Farmaceutica — Federchimica/Farmindustria/Assistal | Industria | ~210k | ✅ | ✅ | 🤖 |
67
+ | 5 | Turismo — Confcommercio | Terziario | ~300k | ✅ | ✅ | 🤖 |
68
+ | 6 | Edilizia — ANCE | Edilizia | ~550k | ✅ | ✅ | 🤖 |
69
+ | 7 | Cooperative Sociali — Confcooperative/Legacoop/AGCI | Terziario | ~380k | ✅ | ✅ | 🤖 |
70
+ | 8 | Logistica, Trasporto Merci e Spedizione — Confetra | Industria | ~430k | ✅ | ✅ | 🤖 |
71
+ | 9 | Servizi di Pulizia e Multiservizi — ANIP-Confindustria | Terziario | ~580k | ✅ | ✅ | 🤖 |
72
+ | 10 | Studi e Attività Professionali — Confprofessioni | Terziario | ~350k | ✅ | ✅ | 🤖 |
73
+ | 11 | Credito — ABI | Credito | ~270k | ✅ | ✅ | 🤖 |
74
+ | 12 | Tessile Abbigliamento Moda — SMI | Industria | ~160k | ✅ | ✅ | 🤖 |
75
+ | 13 | Alimentari Industria — Federalimentare | Industria | ~145k | ✅ | ✅ | 🤖 |
76
+ | 14 | Distribuzione Moderna Organizzata — Federdistribuzione | Terziario | ~460k | ✅ | ✅ | 🤖 |
77
+ | 15 | Metalmeccanica e Installazione Impianti — Artigianato | Artigianato | ~350k | ✅ | ✅ | 🤖 |
78
+ | 16 | Gomma e Plastica Industria — Federazione Gomma Plastica | Industria | ~90k | ✅ | ✅ | 🤖 |
79
+ | 17 | Grafica e Editoria — AIEG-Acigraf | Industria | ~70k | ✅ | ✅ | 🤖 |
80
+ | 18 | Carta e Cartone — Assocarta | Industria | ~35k | ✅ | ✅ | 🤖 |
81
+ | 19 | Telecomunicazioni — Asstel | Industria | ~110k | ✅ | ✅ | 🤖 |
82
+ | 20 | Vigilanza Privata — ASSIV/ANIVP/UNIV (GPG) | Terziario | ~85k | ✅ | ✅ | 🤖 |
83
+ | 21 | Legno e Arredamento — Federlegno-Arredo | Industria | ~90k | ✅ | ✅ | 🤖 |
84
+ | 22 | Edilizia e Affini — CNA/Confartigianato/Casartigiani | Artigianato | ~350k | ✅ | ✅ | 🤖 |
85
+ | 23 | Gas e Acqua — Utilitalia/Proxigas/Anfida/Assogas | Industria | ~65k | ✅ | ✅ | 🤖 |
86
+ | 24 | Istituzioni Socio-Assistenziali — UNEBA | Terziario | ~130k | ✅ | ✅ | 🤖 |
87
+ | 25 | Acconciatura ed Estetica — Confartigianato/CNA | Artigianato | ~95k | ✅ | ✅ | 🤖 |
88
+ | 26 | Area Alimentazione e Panificazione — Artigianato (Confartigianato/CNA) | Artigianato | ~90k | ✅ | ✅ | 🤖 |
89
+ | 27 | Autoferrotranvieri e Internavigatori (Mobilita/TPL) — AGENS/ASSTRA/ANAV | Terziario | ~120k | ✅ | ✅ | 🤖 |
90
+ | 28 | Credito Cooperativo (BCC/CRA) — Federcasse | Credito | ~33k | ✅ | ✅ | 🤖 |
91
+ | 29 | Elettrico (produzione/distribuzione energia) — Elettricita Futura | Industria | ~60k | ✅ | ✅ | 🤖 |
92
+ | 30 | Calzaturiero (industria delle calzature) — Assocalzaturifici | Industria | ~75k | ✅ | ✅ | 🤖 |
93
+ | 31 | Area Tessile-Moda e Chimica-Ceramica — Artigianato (Confartigianato/CNA) | Artigianato | ~120k | ✅ | ✅ | 🤖 |
94
+ | 32 | Area Legno-Lapidei — Artigianato (Confartigianato/CNA) | Artigianato | ~95k | ✅ | ✅ | 🤖 |
95
+ | 33 | Area Comunicazione — Artigianato (Confartigianato/CNA) | Artigianato | ~60k | ✅ | ✅ | 🤖 |
96
+ | 34 | Ceramica Industria — Confindustria Ceramica (Assopiastrelle) | Industria | ~23k | ✅ | ✅ | 🤖 |
97
+ | 35 | Orafi e Argentieri — Federorafi | Industria | ~18k | ✅ | ✅ | 🤖 |
98
+ | 36 | Pelli e Cuoio Industria — Assopellettieri | Industria | ~17k | ✅ | ✅ | 🤖 |
99
+ | 37 | Pubblici Esercizi, Ristorazione Collettiva e Turismo — FIPE/ANGEM | Terziario | ~350k | ✅ | ✅ | 🤖 |
100
+ | 38 | Agenzie di Viaggio e Turismo — Fiavet/Confcommercio | Terziario | ~25k | ✅ | ✅ | 🤖 |
101
+ | 39 | Terziario Distribuzione e Servizi — Confesercenti | Terziario | ~230k | ✅ | ✅ | 🤖 |
102
+ | 40 | Turismo — Federalberghi/Faita | Terziario | ~220k | ✅ | ✅ | 🤖 |
103
+ | 41 | Funzioni Centrali 2022-2024 — ARAN (Ministeri, Agenzie, INPS, INAIL) | Pubblica Amministrazione | ~250k | ✅ | ✅ | 🤖 |
104
+ | 42 | Funzioni Locali 2022-2024 — ARAN (Comuni, Province, Regioni, Camere di Commercio) | Pubblica Amministrazione | ~400k | ✅ | ✅ | 🤖 |
105
+ | 43 | Comparto Sanità 2022-2024 — ARAN (SSN non-dirigenza) | Pubblica Amministrazione | ~580k | ✅ | ✅ | 🤖 |
106
+ | 44 | Area Sanità 2022-2024 — ARAN (Dirigenti Medici e Veterinari SSN) | Pubblica Amministrazione | ~100k | ✅ | ✅ | 🤖 |
107
+ | 45 | Area Sanità 2022-2024 — ARAN (Dirigenti Sanitari: psicologi, farmacisti, biologi) | Pubblica Amministrazione | ~37k | ✅ | ✅ | 🤖 |
108
+ | 46 | Area Dirigenza Funzioni Locali 2022-2024 — ARAN (Dirigenti enti locali, Segretari comunali) | Pubblica Amministrazione | ~13k | ✅ | ✅ | 🤖 |
109
+ | 47 | Area Dirigenza Funzioni Centrali 2022-2024 — ARAN (Dirigenti ministeri, agenzie fiscali, INPS, INAIL) | Pubblica Amministrazione | ~30k | ✅ | ✅ | 🤖 |
110
+ | 48 | Area Dirigenza Istruzione e Ricerca 2022-2024 — ARAN (Dirigenti scolastici, universitari, ricerca) | Pubblica Amministrazione | ~8k | ✅ | ✅ | 🤖 |
111
+ | 49 | Comparto Istruzione e Ricerca 2022-2024 — ARAN (Docenti e ATA scuola, università, ricerca) | Pubblica Amministrazione | ~1,2M | ✅ | ✅ | 🤖 |
112
+ | 50 | Case di Cura Private - Personale Non Medico (AIOP/ARIS) | Sanità privata | ~150k | ✅ | ✅ | 🤖 |
113
+ | 51 | Lavoro Domestico — DOMINA/FIDALDO/ASSINDATCOLF (conviventi) | Lavoro Domestico | ~900k | ✅ | ✅ | 🤖 |
114
+ | 52 | Lavoro Domestico — DOMINA/FIDALDO/ASSINDATCOLF (non conviventi) | Lavoro Domestico | ~900k | ✅ | ✅ | 🤖 |
115
+ | 53 | Operai Agricoli e Florovivaisti — Coldiretti/Confagricoltura/CIA | Agricoltura | ~600k | ✅ | ✅ | 🤖 |
116
+ | 54 | Chimica e Affini PMI — Unionchimica Confapi | Chimica | ~56k | ✅ | ✅ | 🤖 |
117
+ | 55 | Panificazione e Settori Affini Industria — Assipan/Fiesa/Federpanificatori | Alimentare | ~20k | ✅ | ✅ | 🤖 |
118
+ | 56 | Attività Ferroviarie — AGENS | Trasporto | ~75k | ✅ | ✅ | 🤖 |
119
+ | 57 | Trasporto Aereo — Gestori Aeroportuali (Assaeroporti) | Trasporto | ~40k | ✅ | ✅ | 🤖 |
120
+ | 58 | Igiene Ambientale — Servizi Ambientali e di Igiene Urbana (Utilitalia/FISE) | Industria | ~65k | ✅ | ✅ | 🤖 |
121
+ | 59 | Impiegati e Tecnici Agricoli — Confagricoltura/CIA/Coldiretti | Agricoltura | ~80k | ✅ | ✅ | 🤖 |
122
+
123
+ <p id="fn-1"><a href="#ref-1">1.</a> Approximate estimates. Sources: CNEL, INPS, Ministero del Lavoro, CCNL renewal communications.</p>
124
+
125
+ <p id="fn-2"><a href="#ref-2">2.</a> Salary tables were extracted from official CCNL documents using Claude Code (AI-assisted), without manual human review. Values should be verified against the official source before use in production payroll systems.</p>
126
+
127
+ The 59 contracts above cover approximately **14.3 million workers** (sum of per-CCNL estimates; some overlap is possible where sector boundaries are not mutually exclusive, so the unique-worker count is somewhat lower). Italy has roughly **16 million employees** covered by some collective agreement (ISTAT/CNEL 2024, public and private sectors combined). This library therefore reaches an estimated **~85–90% of CCNL-covered workers** on a gross-headcount basis.
128
+
129
+ ## What is not modelled
130
+
131
+ - Addizionali regionali and addizionali comunali
132
+ - Detrazioni per carichi di famiglia (Art. 12 TUIR)
133
+ - IVS contributory ceiling split (Art. 1 L. 335/1995)
134
+ - Second-level bargaining (territorial and company agreements)
135
+ - Bilateral system contributions (EST, Fon.Te, …)
136
+ - Overtime, night/holiday premiums, leave accruals, sick-pay integrations
137
+
138
+ Each limitation is documented in the relevant data file's `coverage.notes` field and marked with `# SIMPLIFICATION:` comments in the engine source.
139
+
140
+ ## Releasing a new version
141
+
142
+ Releases are fully automated via GitHub Actions. Steps:
143
+
144
+ 1. Bump `version` in `pyproject.toml` following [SemVer](https://semver.org/).
145
+ 2. Commit the change: `git commit -m "chore: bump version to X.Y.Z"`.
146
+ 3. Push a tag: `git tag vX.Y.Z && git push origin vX.Y.Z`.
147
+
148
+ The `release` workflow then: runs the full test suite, builds the wheel and sdist with `uv build`, verifies the tag matches the package version, publishes to PyPI via [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no API token required), and creates a GitHub Release with auto-generated notes and the dist files attached.
149
+
150
+ **Pre-requisite (one-time):** configure a Trusted Publisher on PyPI for this repository under the `pypi` GitHub environment.
151
+
152
+ ## Development
153
+
154
+ This library was built with AI coding assistants — [Claude Code](https://claude.ai/code), [Codex](https://openai.com/codex), and [Opencode](https://opencode.ai). Generated code is covered by the test suite but has not been manually reviewed line by line.
155
+
156
+ ## Disclaimer
157
+
158
+ This library is not legal or tax advice. Figures are computed from publicly available CCNL tables and statutory rates as of the dates indicated in the data files. Always verify results against official sources or a qualified payroll professional.
@@ -0,0 +1,134 @@
1
+ [project]
2
+ name = "ccnl-engine"
3
+ version = "0.1.0"
4
+ description = "Italian CCNL payroll engine: gross-to-net and employer cost from first principles"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [{ name = "Intella", email = "dev@intella.tech" }]
8
+ keywords = ["ccnl", "payroll", "italy", "labor", "hrm"]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Intended Audience :: Developers",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3.12",
15
+ "Topic :: Office/Business :: Financial",
16
+ "Typing :: Typed",
17
+ ]
18
+ requires-python = ">=3.12"
19
+ dependencies = [
20
+ "pydantic>=2.0",
21
+ ]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/lucas-puerari/ccnl-engine"
25
+ Repository = "https://github.com/lucas-puerari/ccnl-engine"
26
+ "Bug Tracker" = "https://github.com/lucas-puerari/ccnl-engine/issues"
27
+
28
+ [dependency-groups]
29
+ dev = [
30
+ "bandit>=1.9.4",
31
+ "complexipy>=8.0",
32
+ "mypy>=2.3.1",
33
+ "pip-audit>=2.10.1",
34
+ "pytest>=9.1.1",
35
+ "pytest-cov>=7.1.0",
36
+ "ruff>=0.16.5",
37
+ ]
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Ruff — linting + formatting
41
+ # ---------------------------------------------------------------------------
42
+ [tool.ruff]
43
+ target-version = "py312"
44
+ line-length = 88
45
+ preview = true
46
+
47
+ [tool.ruff.lint]
48
+ select = ["ALL"]
49
+ ignore = [
50
+ "RUF201", # rule-codes-in-selectors
51
+ "COM812", # missing-trailing-comma
52
+ "ISC001", # single-line-implicit-string-concatenation
53
+ "TRY003", # raise-vanilla-args
54
+ "FBT", # boolean positional args
55
+ "PLR2004", # magic-value-comparison
56
+ "S101", # assert
57
+ "RUF012", # mutable-class-default
58
+ "CPY001", # missing-copyright-notice
59
+ "D413", # missing-blank-line-after-last-section
60
+ "PLR0913", # too-many-arguments
61
+ "PLR0917", # too-many-positional-arguments
62
+ "PLR0914", # too-many-locals
63
+ "PLC2701", # import-private-name
64
+ ]
65
+
66
+ [tool.ruff.lint.per-file-ignores]
67
+ "tests/**" = ["S", "ARG", "SLF001", "PLR6301"] # PLR6301: pytest methods don't need self
68
+ # Pydantic needs runtime access to annotated types; TCH001-TCH003 would move
69
+ # them to TYPE_CHECKING blocks, breaking model_validate and field resolution.
70
+ # Applies only to files that define Pydantic models, not to engine code.
71
+ "src/ccnl_engine/models/**" = ["TCH"]
72
+ "src/ccnl_engine/tax/models.py" = ["TCH"]
73
+
74
+ [tool.ruff.format]
75
+ quote-style = "double"
76
+ indent-style = "space"
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # Pytest
80
+ # ---------------------------------------------------------------------------
81
+ [tool.pytest.ini_options]
82
+ testpaths = ["tests"]
83
+ addopts = "--cov=src/ccnl_engine --cov-report=term-missing --cov-report=xml --cov-fail-under=100 -q"
84
+
85
+ # ---------------------------------------------------------------------------
86
+ # Coverage
87
+ # ---------------------------------------------------------------------------
88
+ [tool.coverage.run]
89
+ source = ["src/ccnl_engine"]
90
+ branch = true
91
+
92
+ [tool.coverage.report]
93
+ show_missing = true
94
+ skip_covered = false
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Bandit — SAST
98
+ # ---------------------------------------------------------------------------
99
+ [tool.bandit]
100
+ targets = ["src"]
101
+ severity = "medium" # report medium and above
102
+ confidence = "medium" # report medium and above
103
+ exclude_dirs = [".venv"]
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # Mypy — strict type checking
107
+ # ---------------------------------------------------------------------------
108
+ [tool.mypy]
109
+ python_version = "3.12"
110
+ strict = true
111
+ warn_unreachable = true
112
+ warn_unused_ignores = true
113
+ show_error_codes = true
114
+ pretty = true
115
+ plugins = ["pydantic.mypy"]
116
+
117
+ [tool.pydantic-mypy]
118
+ init_forbid_extra = true
119
+ init_typed = true
120
+ warn_required_dynamic_aliases = true
121
+
122
+ # ---------------------------------------------------------------------------
123
+ # Package data — include JSON files in wheel/sdist
124
+ # ---------------------------------------------------------------------------
125
+ [tool.setuptools.package-data]
126
+ "ccnl_engine" = ["contracts/data/*.json", "tax/data/*.json"]
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # complexipy — Cognitive Complexity (SonarSource whitepaper v1.7)
130
+ # ---------------------------------------------------------------------------
131
+ [tool.complexipy]
132
+ max-complexity-allowed = 15
133
+ exclude = [".venv", "venv", "build", "dist"]
134
+ failed = true
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ """ccnl_engine package."""
@@ -0,0 +1 @@
1
+ """CCNL contract data and loaders."""
@@ -0,0 +1 @@
1
+ """Bundled CCNL contract JSON data files."""