coati-payroll 0.0.2__py3-none-any.whl

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.

Potentially problematic release.


This version of coati-payroll might be problematic. Click here for more details.

Files changed (243) hide show
  1. coati_payroll/__init__.py +415 -0
  2. coati_payroll/app.py +95 -0
  3. coati_payroll/audit_helpers.py +904 -0
  4. coati_payroll/auth.py +123 -0
  5. coati_payroll/cli.py +1318 -0
  6. coati_payroll/config.py +219 -0
  7. coati_payroll/demo_data.py +813 -0
  8. coati_payroll/enums.py +278 -0
  9. coati_payroll/forms.py +1769 -0
  10. coati_payroll/formula_engine/__init__.py +81 -0
  11. coati_payroll/formula_engine/ast/__init__.py +110 -0
  12. coati_payroll/formula_engine/ast/ast_visitor.py +259 -0
  13. coati_payroll/formula_engine/ast/expression_evaluator.py +228 -0
  14. coati_payroll/formula_engine/ast/safe_operators.py +131 -0
  15. coati_payroll/formula_engine/ast/type_converter.py +172 -0
  16. coati_payroll/formula_engine/data_sources.py +752 -0
  17. coati_payroll/formula_engine/engine.py +247 -0
  18. coati_payroll/formula_engine/exceptions.py +52 -0
  19. coati_payroll/formula_engine/execution/__init__.py +24 -0
  20. coati_payroll/formula_engine/execution/execution_context.py +52 -0
  21. coati_payroll/formula_engine/execution/step_executor.py +62 -0
  22. coati_payroll/formula_engine/execution/variable_store.py +59 -0
  23. coati_payroll/formula_engine/novelty_codes.py +206 -0
  24. coati_payroll/formula_engine/results/__init__.py +20 -0
  25. coati_payroll/formula_engine/results/execution_result.py +59 -0
  26. coati_payroll/formula_engine/steps/__init__.py +30 -0
  27. coati_payroll/formula_engine/steps/assignment_step.py +71 -0
  28. coati_payroll/formula_engine/steps/base_step.py +48 -0
  29. coati_payroll/formula_engine/steps/calculation_step.py +42 -0
  30. coati_payroll/formula_engine/steps/conditional_step.py +122 -0
  31. coati_payroll/formula_engine/steps/step_factory.py +58 -0
  32. coati_payroll/formula_engine/steps/tax_lookup_step.py +45 -0
  33. coati_payroll/formula_engine/tables/__init__.py +24 -0
  34. coati_payroll/formula_engine/tables/bracket_calculator.py +51 -0
  35. coati_payroll/formula_engine/tables/table_lookup.py +161 -0
  36. coati_payroll/formula_engine/tables/tax_table.py +32 -0
  37. coati_payroll/formula_engine/validation/__init__.py +24 -0
  38. coati_payroll/formula_engine/validation/schema_validator.py +37 -0
  39. coati_payroll/formula_engine/validation/security_validator.py +52 -0
  40. coati_payroll/formula_engine/validation/tax_table_validator.py +205 -0
  41. coati_payroll/formula_engine_examples.py +153 -0
  42. coati_payroll/i18n.py +54 -0
  43. coati_payroll/initial_data.py +613 -0
  44. coati_payroll/interes_engine.py +450 -0
  45. coati_payroll/liquidacion_engine/__init__.py +25 -0
  46. coati_payroll/liquidacion_engine/engine.py +267 -0
  47. coati_payroll/locale_config.py +165 -0
  48. coati_payroll/log.py +138 -0
  49. coati_payroll/model.py +2410 -0
  50. coati_payroll/nomina_engine/__init__.py +87 -0
  51. coati_payroll/nomina_engine/calculators/__init__.py +30 -0
  52. coati_payroll/nomina_engine/calculators/benefit_calculator.py +79 -0
  53. coati_payroll/nomina_engine/calculators/concept_calculator.py +254 -0
  54. coati_payroll/nomina_engine/calculators/deduction_calculator.py +105 -0
  55. coati_payroll/nomina_engine/calculators/exchange_rate_calculator.py +51 -0
  56. coati_payroll/nomina_engine/calculators/perception_calculator.py +75 -0
  57. coati_payroll/nomina_engine/calculators/salary_calculator.py +86 -0
  58. coati_payroll/nomina_engine/domain/__init__.py +27 -0
  59. coati_payroll/nomina_engine/domain/calculation_items.py +52 -0
  60. coati_payroll/nomina_engine/domain/employee_calculation.py +53 -0
  61. coati_payroll/nomina_engine/domain/payroll_context.py +44 -0
  62. coati_payroll/nomina_engine/engine.py +188 -0
  63. coati_payroll/nomina_engine/processors/__init__.py +28 -0
  64. coati_payroll/nomina_engine/processors/accounting_processor.py +171 -0
  65. coati_payroll/nomina_engine/processors/accumulation_processor.py +90 -0
  66. coati_payroll/nomina_engine/processors/loan_processor.py +227 -0
  67. coati_payroll/nomina_engine/processors/novelty_processor.py +42 -0
  68. coati_payroll/nomina_engine/processors/vacation_processor.py +67 -0
  69. coati_payroll/nomina_engine/repositories/__init__.py +32 -0
  70. coati_payroll/nomina_engine/repositories/acumulado_repository.py +83 -0
  71. coati_payroll/nomina_engine/repositories/base_repository.py +40 -0
  72. coati_payroll/nomina_engine/repositories/config_repository.py +102 -0
  73. coati_payroll/nomina_engine/repositories/employee_repository.py +34 -0
  74. coati_payroll/nomina_engine/repositories/exchange_rate_repository.py +58 -0
  75. coati_payroll/nomina_engine/repositories/novelty_repository.py +54 -0
  76. coati_payroll/nomina_engine/repositories/planilla_repository.py +52 -0
  77. coati_payroll/nomina_engine/results/__init__.py +24 -0
  78. coati_payroll/nomina_engine/results/error_result.py +28 -0
  79. coati_payroll/nomina_engine/results/payroll_result.py +53 -0
  80. coati_payroll/nomina_engine/results/validation_result.py +39 -0
  81. coati_payroll/nomina_engine/services/__init__.py +22 -0
  82. coati_payroll/nomina_engine/services/accounting_voucher_service.py +708 -0
  83. coati_payroll/nomina_engine/services/employee_processing_service.py +173 -0
  84. coati_payroll/nomina_engine/services/payroll_execution_service.py +374 -0
  85. coati_payroll/nomina_engine/services/snapshot_service.py +295 -0
  86. coati_payroll/nomina_engine/validators/__init__.py +31 -0
  87. coati_payroll/nomina_engine/validators/base_validator.py +48 -0
  88. coati_payroll/nomina_engine/validators/currency_validator.py +50 -0
  89. coati_payroll/nomina_engine/validators/employee_validator.py +87 -0
  90. coati_payroll/nomina_engine/validators/period_validator.py +44 -0
  91. coati_payroll/nomina_engine/validators/planilla_validator.py +136 -0
  92. coati_payroll/plugin_manager.py +176 -0
  93. coati_payroll/queue/__init__.py +33 -0
  94. coati_payroll/queue/driver.py +127 -0
  95. coati_payroll/queue/drivers/__init__.py +22 -0
  96. coati_payroll/queue/drivers/dramatiq_driver.py +268 -0
  97. coati_payroll/queue/drivers/huey_driver.py +390 -0
  98. coati_payroll/queue/drivers/noop_driver.py +54 -0
  99. coati_payroll/queue/selector.py +121 -0
  100. coati_payroll/queue/tasks.py +764 -0
  101. coati_payroll/rate_limiting.py +83 -0
  102. coati_payroll/rbac.py +183 -0
  103. coati_payroll/report_engine.py +512 -0
  104. coati_payroll/report_export.py +208 -0
  105. coati_payroll/schema_validator.py +167 -0
  106. coati_payroll/security.py +77 -0
  107. coati_payroll/static/styles.css +1044 -0
  108. coati_payroll/system_reports.py +573 -0
  109. coati_payroll/templates/auth/login.html +189 -0
  110. coati_payroll/templates/base.html +283 -0
  111. coati_payroll/templates/index.html +227 -0
  112. coati_payroll/templates/macros.html +146 -0
  113. coati_payroll/templates/modules/calculation_rule/form.html +78 -0
  114. coati_payroll/templates/modules/calculation_rule/index.html +102 -0
  115. coati_payroll/templates/modules/calculation_rule/schema_editor.html +1159 -0
  116. coati_payroll/templates/modules/carga_inicial_prestacion/form.html +170 -0
  117. coati_payroll/templates/modules/carga_inicial_prestacion/index.html +170 -0
  118. coati_payroll/templates/modules/carga_inicial_prestacion/reporte.html +193 -0
  119. coati_payroll/templates/modules/config_calculos/index.html +44 -0
  120. coati_payroll/templates/modules/configuracion/index.html +90 -0
  121. coati_payroll/templates/modules/currency/form.html +47 -0
  122. coati_payroll/templates/modules/currency/index.html +64 -0
  123. coati_payroll/templates/modules/custom_field/form.html +62 -0
  124. coati_payroll/templates/modules/custom_field/index.html +78 -0
  125. coati_payroll/templates/modules/deduccion/form.html +1 -0
  126. coati_payroll/templates/modules/deduccion/index.html +1 -0
  127. coati_payroll/templates/modules/employee/form.html +254 -0
  128. coati_payroll/templates/modules/employee/index.html +76 -0
  129. coati_payroll/templates/modules/empresa/form.html +74 -0
  130. coati_payroll/templates/modules/empresa/index.html +71 -0
  131. coati_payroll/templates/modules/exchange_rate/form.html +47 -0
  132. coati_payroll/templates/modules/exchange_rate/import.html +93 -0
  133. coati_payroll/templates/modules/exchange_rate/index.html +114 -0
  134. coati_payroll/templates/modules/liquidacion/index.html +58 -0
  135. coati_payroll/templates/modules/liquidacion/nueva.html +51 -0
  136. coati_payroll/templates/modules/liquidacion/ver.html +91 -0
  137. coati_payroll/templates/modules/payroll_concepts/audit_log.html +146 -0
  138. coati_payroll/templates/modules/percepcion/form.html +1 -0
  139. coati_payroll/templates/modules/percepcion/index.html +1 -0
  140. coati_payroll/templates/modules/planilla/config.html +190 -0
  141. coati_payroll/templates/modules/planilla/config_deducciones.html +129 -0
  142. coati_payroll/templates/modules/planilla/config_empleados.html +116 -0
  143. coati_payroll/templates/modules/planilla/config_percepciones.html +113 -0
  144. coati_payroll/templates/modules/planilla/config_prestaciones.html +118 -0
  145. coati_payroll/templates/modules/planilla/config_reglas.html +120 -0
  146. coati_payroll/templates/modules/planilla/ejecutar_nomina.html +106 -0
  147. coati_payroll/templates/modules/planilla/form.html +197 -0
  148. coati_payroll/templates/modules/planilla/index.html +144 -0
  149. coati_payroll/templates/modules/planilla/listar_nominas.html +91 -0
  150. coati_payroll/templates/modules/planilla/log_nomina.html +135 -0
  151. coati_payroll/templates/modules/planilla/novedades/form.html +177 -0
  152. coati_payroll/templates/modules/planilla/novedades/index.html +170 -0
  153. coati_payroll/templates/modules/planilla/ver_nomina.html +477 -0
  154. coati_payroll/templates/modules/planilla/ver_nomina_empleado.html +231 -0
  155. coati_payroll/templates/modules/plugins/index.html +71 -0
  156. coati_payroll/templates/modules/prestacion/form.html +1 -0
  157. coati_payroll/templates/modules/prestacion/index.html +1 -0
  158. coati_payroll/templates/modules/prestacion_management/dashboard.html +150 -0
  159. coati_payroll/templates/modules/prestacion_management/initial_balance_bulk.html +195 -0
  160. coati_payroll/templates/modules/prestamo/approve.html +156 -0
  161. coati_payroll/templates/modules/prestamo/condonacion.html +249 -0
  162. coati_payroll/templates/modules/prestamo/detail.html +443 -0
  163. coati_payroll/templates/modules/prestamo/form.html +203 -0
  164. coati_payroll/templates/modules/prestamo/index.html +150 -0
  165. coati_payroll/templates/modules/prestamo/pago_extraordinario.html +211 -0
  166. coati_payroll/templates/modules/prestamo/tabla_pago_pdf.html +181 -0
  167. coati_payroll/templates/modules/report/admin_index.html +125 -0
  168. coati_payroll/templates/modules/report/detail.html +129 -0
  169. coati_payroll/templates/modules/report/execute.html +266 -0
  170. coati_payroll/templates/modules/report/index.html +95 -0
  171. coati_payroll/templates/modules/report/permissions.html +64 -0
  172. coati_payroll/templates/modules/settings/index.html +274 -0
  173. coati_payroll/templates/modules/shared/concept_form.html +201 -0
  174. coati_payroll/templates/modules/shared/concept_index.html +145 -0
  175. coati_payroll/templates/modules/tipo_planilla/form.html +70 -0
  176. coati_payroll/templates/modules/tipo_planilla/index.html +68 -0
  177. coati_payroll/templates/modules/user/form.html +65 -0
  178. coati_payroll/templates/modules/user/index.html +76 -0
  179. coati_payroll/templates/modules/user/profile.html +81 -0
  180. coati_payroll/templates/modules/vacation/account_detail.html +149 -0
  181. coati_payroll/templates/modules/vacation/account_form.html +52 -0
  182. coati_payroll/templates/modules/vacation/account_index.html +68 -0
  183. coati_payroll/templates/modules/vacation/dashboard.html +156 -0
  184. coati_payroll/templates/modules/vacation/initial_balance_bulk.html +149 -0
  185. coati_payroll/templates/modules/vacation/initial_balance_form.html +93 -0
  186. coati_payroll/templates/modules/vacation/leave_request_detail.html +158 -0
  187. coati_payroll/templates/modules/vacation/leave_request_form.html +61 -0
  188. coati_payroll/templates/modules/vacation/leave_request_index.html +98 -0
  189. coati_payroll/templates/modules/vacation/policy_detail.html +176 -0
  190. coati_payroll/templates/modules/vacation/policy_form.html +152 -0
  191. coati_payroll/templates/modules/vacation/policy_index.html +79 -0
  192. coati_payroll/templates/modules/vacation/register_taken_form.html +178 -0
  193. coati_payroll/translations/en/LC_MESSAGES/messages.mo +0 -0
  194. coati_payroll/translations/en/LC_MESSAGES/messages.po +7283 -0
  195. coati_payroll/translations/es/LC_MESSAGES/messages.mo +0 -0
  196. coati_payroll/translations/es/LC_MESSAGES/messages.po +7374 -0
  197. coati_payroll/vacation_service.py +451 -0
  198. coati_payroll/version.py +18 -0
  199. coati_payroll/vistas/__init__.py +64 -0
  200. coati_payroll/vistas/calculation_rule.py +307 -0
  201. coati_payroll/vistas/carga_inicial_prestacion.py +423 -0
  202. coati_payroll/vistas/config_calculos.py +72 -0
  203. coati_payroll/vistas/configuracion.py +87 -0
  204. coati_payroll/vistas/constants.py +17 -0
  205. coati_payroll/vistas/currency.py +112 -0
  206. coati_payroll/vistas/custom_field.py +120 -0
  207. coati_payroll/vistas/employee.py +305 -0
  208. coati_payroll/vistas/empresa.py +153 -0
  209. coati_payroll/vistas/exchange_rate.py +341 -0
  210. coati_payroll/vistas/liquidacion.py +205 -0
  211. coati_payroll/vistas/payroll_concepts.py +580 -0
  212. coati_payroll/vistas/planilla/__init__.py +38 -0
  213. coati_payroll/vistas/planilla/association_routes.py +238 -0
  214. coati_payroll/vistas/planilla/config_routes.py +158 -0
  215. coati_payroll/vistas/planilla/export_routes.py +175 -0
  216. coati_payroll/vistas/planilla/helpers/__init__.py +34 -0
  217. coati_payroll/vistas/planilla/helpers/association_helpers.py +161 -0
  218. coati_payroll/vistas/planilla/helpers/excel_helpers.py +29 -0
  219. coati_payroll/vistas/planilla/helpers/form_helpers.py +97 -0
  220. coati_payroll/vistas/planilla/nomina_routes.py +488 -0
  221. coati_payroll/vistas/planilla/novedad_routes.py +227 -0
  222. coati_payroll/vistas/planilla/routes.py +145 -0
  223. coati_payroll/vistas/planilla/services/__init__.py +26 -0
  224. coati_payroll/vistas/planilla/services/export_service.py +687 -0
  225. coati_payroll/vistas/planilla/services/nomina_service.py +233 -0
  226. coati_payroll/vistas/planilla/services/novedad_service.py +126 -0
  227. coati_payroll/vistas/planilla/services/planilla_service.py +34 -0
  228. coati_payroll/vistas/planilla/validators/__init__.py +18 -0
  229. coati_payroll/vistas/planilla/validators/planilla_validators.py +40 -0
  230. coati_payroll/vistas/plugins.py +45 -0
  231. coati_payroll/vistas/prestacion.py +272 -0
  232. coati_payroll/vistas/prestamo.py +808 -0
  233. coati_payroll/vistas/report.py +432 -0
  234. coati_payroll/vistas/settings.py +29 -0
  235. coati_payroll/vistas/tipo_planilla.py +134 -0
  236. coati_payroll/vistas/user.py +172 -0
  237. coati_payroll/vistas/vacation.py +1045 -0
  238. coati_payroll-0.0.2.dist-info/LICENSE +201 -0
  239. coati_payroll-0.0.2.dist-info/METADATA +581 -0
  240. coati_payroll-0.0.2.dist-info/RECORD +243 -0
  241. coati_payroll-0.0.2.dist-info/WHEEL +5 -0
  242. coati_payroll-0.0.2.dist-info/entry_points.txt +2 -0
  243. coati_payroll-0.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,206 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Mapping of novelty codes to their calculation behavior.
15
+
16
+ This module defines the mapping between novelty codes and their
17
+ calculation behavior (perception/deduction, gravable, etc.).
18
+ """
19
+
20
+ NOVELTY_CODES = {
21
+ "HORAS_EXTRA": {
22
+ "tipo": "percepcion",
23
+ "gravable": True,
24
+ "descripcion": "Horas extra trabajadas",
25
+ },
26
+ "HORAS_EXTRA_DOBLES": {
27
+ "tipo": "percepcion",
28
+ "gravable": True,
29
+ "descripcion": "Horas extra dobles (feriados/domingos)",
30
+ },
31
+ "AUSENCIA": {
32
+ "tipo": "deduccion",
33
+ "gravable": False,
34
+ "descripcion": "Ausencia no justificada",
35
+ },
36
+ "INCAPACIDAD": {
37
+ "tipo": "deduccion",
38
+ "gravable": False,
39
+ "descripcion": "Incapacidad médica",
40
+ },
41
+ "COMISION": {
42
+ "tipo": "percepcion",
43
+ "gravable": True,
44
+ "descripcion": "Comisiones por ventas",
45
+ },
46
+ "BONIFICACION": {
47
+ "tipo": "percepcion",
48
+ "gravable": True,
49
+ "descripcion": "Bonificación",
50
+ },
51
+ "VIATICO": {
52
+ "tipo": "percepcion",
53
+ "gravable": False,
54
+ "descripcion": "Viáticos",
55
+ },
56
+ "VACACIONES": {
57
+ "tipo": "percepcion",
58
+ "gravable": True,
59
+ "descripcion": "Pago de vacaciones",
60
+ },
61
+ "ADELANTO": {
62
+ "tipo": "deduccion",
63
+ "gravable": False,
64
+ "descripcion": "Adelanto de salario",
65
+ },
66
+ "PRESTAMO": {
67
+ "tipo": "deduccion",
68
+ "gravable": False,
69
+ "descripcion": "Cuota de préstamo",
70
+ },
71
+ # A. Compensación Base y Directa
72
+ "BONO_OBJETIVOS": {
73
+ "tipo": "percepcion",
74
+ "gravable": True,
75
+ "descripcion": "Bono por cumplimiento de objetivos",
76
+ },
77
+ "BONO_ANUAL": {
78
+ "tipo": "percepcion",
79
+ "gravable": True,
80
+ "descripcion": "Bono anual o trimestral",
81
+ },
82
+ "PLUS_PELIGROSIDAD": {
83
+ "tipo": "percepcion",
84
+ "gravable": True,
85
+ "descripcion": "Plus por peligrosidad o toxicidad",
86
+ },
87
+ "PLUS_NOCTURNO": {
88
+ "tipo": "percepcion",
89
+ "gravable": True,
90
+ "descripcion": "Plus por trabajo nocturno",
91
+ },
92
+ "PLUS_ANTIGUEDAD": {
93
+ "tipo": "percepcion",
94
+ "gravable": True,
95
+ "descripcion": "Plus por antigüedad",
96
+ },
97
+ # B. Compensaciones en Especie y Beneficios
98
+ "USO_VEHICULO": {
99
+ "tipo": "percepcion",
100
+ "gravable": True,
101
+ "descripcion": "Uso de vehículo de empresa",
102
+ },
103
+ "SEGURO_SALUD": {
104
+ "tipo": "percepcion",
105
+ "gravable": False,
106
+ "descripcion": "Seguro de salud privado",
107
+ },
108
+ "APORTE_PENSION": {
109
+ "tipo": "percepcion",
110
+ "gravable": False,
111
+ "descripcion": "Aporte patronal a pensión/retiro",
112
+ },
113
+ "STOCK_OPTIONS": {
114
+ "tipo": "percepcion",
115
+ "gravable": True,
116
+ "descripcion": "Opciones de compra de acciones",
117
+ },
118
+ "SUBSIDIO_ALIMENTACION": {
119
+ "tipo": "percepcion",
120
+ "gravable": False,
121
+ "descripcion": "Subsidio de alimentación",
122
+ },
123
+ "SUBSIDIO_TRANSPORTE": {
124
+ "tipo": "percepcion",
125
+ "gravable": False,
126
+ "descripcion": "Subsidio de transporte",
127
+ },
128
+ "SUBSIDIO_GUARDERIA": {
129
+ "tipo": "percepcion",
130
+ "gravable": False,
131
+ "descripcion": "Subsidio de guardería",
132
+ },
133
+ # C. Compensaciones por Tiempo y Bienestar
134
+ "PAGO_FESTIVOS": {
135
+ "tipo": "percepcion",
136
+ "gravable": True,
137
+ "descripcion": "Pago por días festivos trabajados",
138
+ },
139
+ "THIRTEENTH_SALARY": {
140
+ "tipo": "percepcion",
141
+ "gravable": True,
142
+ "descripcion": "Aguinaldo o gratificación anual",
143
+ },
144
+ "UTILIDADES": {
145
+ "tipo": "percepcion",
146
+ "gravable": True,
147
+ "descripcion": "Participación en utilidades",
148
+ },
149
+ "PERMISO_PAGADO": {
150
+ "tipo": "percepcion",
151
+ "gravable": True,
152
+ "descripcion": "Permisos pagados (enfermedad, maternidad, etc.)",
153
+ },
154
+ "FONDO_AHORRO_EMPRESA": {
155
+ "tipo": "percepcion",
156
+ "gravable": False,
157
+ "descripcion": "Aporte empresa a fondo de ahorro",
158
+ },
159
+ "FONDO_AHORRO_EMPLEADO": {
160
+ "tipo": "deduccion",
161
+ "gravable": False,
162
+ "descripcion": "Aporte empleado a fondo de ahorro",
163
+ },
164
+ # D. Reembolsos y Dietas
165
+ "GASTOS_REPRESENTACION": {
166
+ "tipo": "percepcion",
167
+ "gravable": False,
168
+ "descripcion": "Gastos de representación",
169
+ },
170
+ "REEMBOLSO_FORMACION": {
171
+ "tipo": "percepcion",
172
+ "gravable": False,
173
+ "descripcion": "Reembolso de gastos de formación",
174
+ },
175
+ "REEMBOLSO_MEDICO": {
176
+ "tipo": "percepcion",
177
+ "gravable": False,
178
+ "descripcion": "Reembolso de gastos médicos",
179
+ },
180
+ # E. Pagos por Eventos Específicos
181
+ "INDEMNIZACION": {
182
+ "tipo": "percepcion",
183
+ "gravable": True,
184
+ "descripcion": "Indemnización por despido",
185
+ },
186
+ "COMPENSACION_REUBICACION": {
187
+ "tipo": "percepcion",
188
+ "gravable": False,
189
+ "descripcion": "Compensación por reubicación",
190
+ },
191
+ "PREMIO_PUNTUALIDAD": {
192
+ "tipo": "percepcion",
193
+ "gravable": True,
194
+ "descripcion": "Premio por puntualidad/asistencia",
195
+ },
196
+ "PREMIO_INNOVACION": {
197
+ "tipo": "percepcion",
198
+ "gravable": True,
199
+ "descripcion": "Premio por ideas innovadoras",
200
+ },
201
+ "AYUDA_FALLECIMIENTO": {
202
+ "tipo": "percepcion",
203
+ "gravable": False,
204
+ "descripcion": "Ayuda por fallecimiento de familiar",
205
+ },
206
+ }
@@ -0,0 +1,20 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Result DTOs for formula engine execution."""
15
+
16
+ from .execution_result import ExecutionResult
17
+
18
+ __all__ = [
19
+ "ExecutionResult",
20
+ ]
@@ -0,0 +1,59 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Execution result DTO."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from decimal import Decimal
19
+ from typing import Any
20
+
21
+
22
+ class ExecutionResult:
23
+ """Result of formula execution."""
24
+
25
+ def __init__(
26
+ self,
27
+ variables: dict[str, Decimal],
28
+ step_results: dict[str, Any],
29
+ final_output: Decimal,
30
+ ):
31
+ """Initialize execution result.
32
+
33
+ Args:
34
+ variables: All variables after execution
35
+ step_results: Results from each step
36
+ final_output: Final output value
37
+ """
38
+ self.variables = variables
39
+ self.step_results = step_results
40
+ self.final_output = final_output
41
+
42
+ def to_dict(self) -> dict[str, Any]:
43
+ """Convert result to dictionary format.
44
+
45
+ Returns:
46
+ Dictionary representation of result
47
+ """
48
+ return {
49
+ "variables": {k: float(v) for k, v in self.variables.items()},
50
+ "results": {
51
+ k: (
52
+ float(v)
53
+ if isinstance(v, Decimal)
54
+ else ({kk: float(vv) for kk, vv in v.items()} if isinstance(v, dict) else v)
55
+ )
56
+ for k, v in self.step_results.items()
57
+ },
58
+ "output": float(self.final_output),
59
+ }
@@ -0,0 +1,30 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Step execution modules using Strategy pattern."""
15
+
16
+ from .base_step import Step
17
+ from .calculation_step import CalculationStep
18
+ from .conditional_step import ConditionalStep
19
+ from .tax_lookup_step import TaxLookupStep
20
+ from .assignment_step import AssignmentStep
21
+ from .step_factory import StepFactory
22
+
23
+ __all__ = [
24
+ "Step",
25
+ "CalculationStep",
26
+ "ConditionalStep",
27
+ "TaxLookupStep",
28
+ "AssignmentStep",
29
+ "StepFactory",
30
+ ]
@@ -0,0 +1,71 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Assignment step implementation."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from decimal import Decimal
19
+ from typing import TYPE_CHECKING, Any
20
+
21
+ from ..ast.type_converter import to_decimal
22
+ from .base_step import Step
23
+
24
+ if TYPE_CHECKING:
25
+ from ..execution.execution_context import ExecutionContext
26
+
27
+
28
+ class AssignmentStep(Step):
29
+ """Step for assigning values to variables."""
30
+
31
+ def execute(self, context: "ExecutionContext") -> Decimal:
32
+ """Execute assignment step.
33
+
34
+ Args:
35
+ context: Execution context
36
+
37
+ Returns:
38
+ Assigned value as Decimal
39
+ """
40
+ value = self.config.get("value")
41
+ result = self._resolve_value(value, context)
42
+ return result
43
+
44
+ def _resolve_value(self, value: Any, context: "ExecutionContext") -> Decimal:
45
+ """Resolve a value that might be a variable reference.
46
+
47
+ Args:
48
+ value: Value or variable name to resolve
49
+ context: Execution context
50
+
51
+ Returns:
52
+ Decimal value
53
+ """
54
+ if isinstance(value, str) and value in context.variables:
55
+ resolved = context.variables[value]
56
+ if context.trace_callback:
57
+ from coati_payroll.i18n import _
58
+
59
+ context.trace_callback(
60
+ _("Resolviendo variable '%(name)s' => %(value)s") % {"name": value, "value": resolved}
61
+ )
62
+ return resolved
63
+
64
+ resolved_literal = to_decimal(value)
65
+ if context.trace_callback:
66
+ from coati_payroll.i18n import _
67
+
68
+ context.trace_callback(
69
+ _("Resolviendo valor literal '%(raw)s' => %(value)s") % {"raw": value, "value": resolved_literal}
70
+ )
71
+ return resolved_literal
@@ -0,0 +1,48 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Base step interface for Strategy pattern."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from abc import ABC, abstractmethod
19
+ from typing import Any, TYPE_CHECKING
20
+
21
+ if TYPE_CHECKING:
22
+ from ..execution.execution_context import ExecutionContext
23
+
24
+
25
+ class Step(ABC):
26
+ """Base interface for all step types."""
27
+
28
+ def __init__(self, name: str, config: dict[str, Any]):
29
+ """Initialize step.
30
+
31
+ Args:
32
+ name: Step name
33
+ config: Step configuration dictionary
34
+ """
35
+ self.name = name
36
+ self.config = config
37
+
38
+ @abstractmethod
39
+ def execute(self, context: "ExecutionContext") -> Any:
40
+ """Execute the step and return result.
41
+
42
+ Args:
43
+ context: Execution context with variables and tax tables
44
+
45
+ Returns:
46
+ Step execution result
47
+ """
48
+ pass
@@ -0,0 +1,42 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Calculation step implementation."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from decimal import Decimal
19
+ from typing import TYPE_CHECKING
20
+
21
+ from ..ast.expression_evaluator import ExpressionEvaluator
22
+ from .base_step import Step
23
+
24
+ if TYPE_CHECKING:
25
+ from ..execution.execution_context import ExecutionContext
26
+
27
+
28
+ class CalculationStep(Step):
29
+ """Step for executing mathematical calculations."""
30
+
31
+ def execute(self, context: "ExecutionContext") -> Decimal:
32
+ """Execute calculation step.
33
+
34
+ Args:
35
+ context: Execution context
36
+
37
+ Returns:
38
+ Calculated result as Decimal
39
+ """
40
+ formula = self.config.get("formula", "")
41
+ evaluator = ExpressionEvaluator(variables=context.variables, trace_callback=context.trace_callback)
42
+ return evaluator.evaluate(formula)
@@ -0,0 +1,122 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Conditional step implementation."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from decimal import Decimal
19
+ from typing import TYPE_CHECKING, Any
20
+
21
+ from ..ast.expression_evaluator import ExpressionEvaluator
22
+ from ..ast.safe_operators import COMPARISON_OPERATORS
23
+ from ..ast.type_converter import to_decimal
24
+ from ..exceptions import CalculationError
25
+ from .base_step import Step
26
+
27
+ if TYPE_CHECKING:
28
+ from ..execution.execution_context import ExecutionContext
29
+
30
+
31
+ class ConditionalStep(Step):
32
+ """Step for conditional logic (if/else)."""
33
+
34
+ def execute(self, context: "ExecutionContext") -> Decimal:
35
+ """Execute conditional step.
36
+
37
+ Args:
38
+ context: Execution context
39
+
40
+ Returns:
41
+ Result of selected branch as Decimal
42
+ """
43
+ condition = self.config.get("condition", {})
44
+ if_true = self.config.get("if_true", "0")
45
+ if_false = self.config.get("if_false", "0")
46
+
47
+ # Evaluate condition
48
+ condition_result = self._evaluate_condition(condition, context)
49
+
50
+ # Select expression based on condition
51
+ selected_value = if_true if condition_result else if_false
52
+
53
+ # Evaluate selected expression
54
+ evaluator = ExpressionEvaluator(variables=context.variables, trace_callback=context.trace_callback)
55
+ return evaluator.evaluate(str(selected_value))
56
+
57
+ def _evaluate_condition(self, condition: dict[str, Any], context: "ExecutionContext") -> bool:
58
+ """Evaluate a conditional expression.
59
+
60
+ Args:
61
+ condition: Dictionary with 'left', 'operator', 'right' keys
62
+ context: Execution context
63
+
64
+ Returns:
65
+ Boolean result of the condition
66
+
67
+ Raises:
68
+ CalculationError: If condition is invalid
69
+ """
70
+ if not isinstance(condition, dict):
71
+ raise CalculationError("Condition must be a dictionary")
72
+
73
+ left = condition.get("left")
74
+ op = condition.get("operator")
75
+ right = condition.get("right")
76
+
77
+ if op not in COMPARISON_OPERATORS:
78
+ raise CalculationError(f"Invalid comparison operator: {op}")
79
+
80
+ # Resolve variable references
81
+ left_val = self._resolve_value(left, context)
82
+ right_val = self._resolve_value(right, context)
83
+ result = COMPARISON_OPERATORS[op](left_val, right_val)
84
+
85
+ if context.trace_callback:
86
+ from coati_payroll.i18n import _
87
+
88
+ context.trace_callback(
89
+ _("Condición evaluada: %(left)s %(op)s %(right)s -> %(res)s")
90
+ % {"left": left_val, "op": op, "right": right_val, "res": result}
91
+ )
92
+
93
+ return result
94
+
95
+ def _resolve_value(self, value: Any, context: "ExecutionContext") -> Decimal:
96
+ """Resolve a value that might be a variable reference.
97
+
98
+ Args:
99
+ value: Value or variable name to resolve
100
+ context: Execution context
101
+
102
+ Returns:
103
+ Decimal value
104
+ """
105
+ if isinstance(value, str) and value in context.variables:
106
+ resolved = context.variables[value]
107
+ if context.trace_callback:
108
+ from coati_payroll.i18n import _
109
+
110
+ context.trace_callback(
111
+ _("Resolviendo variable '%(name)s' => %(value)s") % {"name": value, "value": resolved}
112
+ )
113
+ return resolved
114
+
115
+ resolved_literal = to_decimal(value)
116
+ if context.trace_callback:
117
+ from coati_payroll.i18n import _
118
+
119
+ context.trace_callback(
120
+ _("Resolviendo valor literal '%(raw)s' => %(value)s") % {"raw": value, "value": resolved_literal}
121
+ )
122
+ return resolved_literal
@@ -0,0 +1,58 @@
1
+ # Copyright 2025 BMO Soluciones, S.A.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Factory for creating step instances."""
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Any
19
+
20
+ from coati_payroll.enums import StepType
21
+
22
+ from ..exceptions import CalculationError
23
+ from .assignment_step import AssignmentStep
24
+ from .base_step import Step
25
+ from .calculation_step import CalculationStep
26
+ from .conditional_step import ConditionalStep
27
+ from .tax_lookup_step import TaxLookupStep
28
+
29
+
30
+ class StepFactory:
31
+ """Factory for creating step instances."""
32
+
33
+ @staticmethod
34
+ def create_step(step_config: dict[str, Any]) -> Step:
35
+ """Create a step instance from configuration.
36
+
37
+ Args:
38
+ step_config: Step configuration dictionary
39
+
40
+ Returns:
41
+ Step instance
42
+
43
+ Raises:
44
+ CalculationError: If step type is unknown
45
+ """
46
+ step_type = step_config.get("type")
47
+ step_name = step_config.get("name", "unnamed_step")
48
+
49
+ if step_type == StepType.CALCULATION:
50
+ return CalculationStep(step_name, step_config)
51
+ elif step_type == StepType.CONDITIONAL:
52
+ return ConditionalStep(step_name, step_config)
53
+ elif step_type == StepType.TAX_LOOKUP:
54
+ return TaxLookupStep(step_name, step_config)
55
+ elif step_type == StepType.ASSIGNMENT:
56
+ return AssignmentStep(step_name, step_config)
57
+ else:
58
+ raise CalculationError(f"Unknown step type: {step_type}")