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,161 @@
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
+ """Helper functions for managing planilla associations."""
15
+
16
+ from sqlalchemy import func, select
17
+ from coati_payroll.model import (
18
+ db,
19
+ PlanillaEmpleado,
20
+ PlanillaIngreso,
21
+ PlanillaDeduccion,
22
+ PlanillaPrestacion,
23
+ PlanillaReglaCalculo,
24
+ )
25
+
26
+
27
+ def get_planilla_component_counts(planilla_id: str) -> dict:
28
+ """Get counts of all components associated with a planilla.
29
+
30
+ Returns a dictionary with counts for empleados, percepciones, deducciones,
31
+ prestaciones, and reglas.
32
+ """
33
+ return {
34
+ "empleados_count": db.session.execute(
35
+ select(func.count()).select_from(PlanillaEmpleado).filter_by(planilla_id=planilla_id)
36
+ ).scalar(),
37
+ "percepciones_count": db.session.execute(
38
+ select(func.count()).select_from(PlanillaIngreso).filter_by(planilla_id=planilla_id)
39
+ ).scalar(),
40
+ "deducciones_count": db.session.execute(
41
+ select(func.count()).select_from(PlanillaDeduccion).filter_by(planilla_id=planilla_id)
42
+ ).scalar(),
43
+ "prestaciones_count": db.session.execute(
44
+ select(func.count()).select_from(PlanillaPrestacion).filter_by(planilla_id=planilla_id)
45
+ ).scalar(),
46
+ "reglas_count": db.session.execute(
47
+ select(func.count()).select_from(PlanillaReglaCalculo).filter_by(planilla_id=planilla_id)
48
+ ).scalar(),
49
+ }
50
+
51
+
52
+ def agregar_asociacion(
53
+ planilla_id: str,
54
+ tipo_componente: str,
55
+ componente_id: str,
56
+ datos_extra: dict | None = None,
57
+ usuario: str | None = None,
58
+ ) -> tuple[bool, str | None, str | None]:
59
+ """Generic function to add any component association to a planilla.
60
+
61
+ Args:
62
+ planilla_id: ID of the planilla
63
+ tipo_componente: Type of component ('percepcion', 'deduccion', 'prestacion', 'regla')
64
+ componente_id: ID of the component to associate
65
+ datos_extra: Additional data for the association (orden, prioridad, etc.)
66
+ usuario: Username of the user creating the association
67
+
68
+ Returns:
69
+ Tuple of (success, error_message, association_id). If success is False, error_message is set.
70
+ """
71
+ from coati_payroll.model import (
72
+ Planilla,
73
+ PlanillaIngreso,
74
+ PlanillaDeduccion,
75
+ PlanillaPrestacion,
76
+ PlanillaReglaCalculo,
77
+ )
78
+
79
+ datos_extra = datos_extra or {}
80
+ usuario = usuario or "system"
81
+
82
+ # Verify planilla exists
83
+ planilla = db.session.get(Planilla, planilla_id)
84
+ if not planilla:
85
+ return False, "Planilla no encontrada", None
86
+
87
+ if not componente_id:
88
+ return False, f"Debe seleccionar una {tipo_componente}.", None
89
+
90
+ # Check for existing association based on type
91
+ existing = None
92
+ association_class = None
93
+ filter_params = {"planilla_id": planilla_id}
94
+
95
+ if tipo_componente == "percepcion":
96
+ association_class = PlanillaIngreso
97
+ filter_params["percepcion_id"] = componente_id
98
+ elif tipo_componente == "deduccion":
99
+ association_class = PlanillaDeduccion
100
+ filter_params["deduccion_id"] = componente_id
101
+ elif tipo_componente == "prestacion":
102
+ association_class = PlanillaPrestacion
103
+ filter_params["prestacion_id"] = componente_id
104
+ elif tipo_componente == "regla":
105
+ association_class = PlanillaReglaCalculo
106
+ filter_params["regla_calculo_id"] = componente_id
107
+ else:
108
+ return False, f"Tipo de componente desconocido: {tipo_componente}", None
109
+
110
+ existing = db.session.execute(db.select(association_class).filter_by(**filter_params)).scalar_one_or_none()
111
+
112
+ if existing:
113
+ return False, f"La {tipo_componente} ya está asignada a esta planilla.", None
114
+
115
+ # Create association based on type
116
+ if tipo_componente == "percepcion":
117
+ orden = datos_extra.get("orden", 0)
118
+ association = PlanillaIngreso(
119
+ planilla_id=planilla_id,
120
+ percepcion_id=componente_id,
121
+ orden=orden,
122
+ editable=True,
123
+ activo=True,
124
+ creado_por=usuario,
125
+ )
126
+ elif tipo_componente == "deduccion":
127
+ prioridad = datos_extra.get("prioridad", 100)
128
+ es_obligatoria = datos_extra.get("es_obligatoria", False)
129
+ association = PlanillaDeduccion(
130
+ planilla_id=planilla_id,
131
+ deduccion_id=componente_id,
132
+ prioridad=prioridad,
133
+ es_obligatoria=es_obligatoria,
134
+ editable=True,
135
+ activo=True,
136
+ creado_por=usuario,
137
+ )
138
+ elif tipo_componente == "prestacion":
139
+ orden = datos_extra.get("orden", 0)
140
+ association = PlanillaPrestacion(
141
+ planilla_id=planilla_id,
142
+ prestacion_id=componente_id,
143
+ orden=orden,
144
+ editable=True,
145
+ activo=True,
146
+ creado_por=usuario,
147
+ )
148
+ elif tipo_componente == "regla":
149
+ orden = datos_extra.get("orden", 0)
150
+ association = PlanillaReglaCalculo(
151
+ planilla_id=planilla_id,
152
+ regla_calculo_id=componente_id,
153
+ orden=orden,
154
+ activo=True,
155
+ creado_por=usuario,
156
+ )
157
+
158
+ db.session.add(association)
159
+ db.session.commit()
160
+
161
+ return True, None, association.id
@@ -0,0 +1,29 @@
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
+ """Excel helper functions for planilla views."""
15
+
16
+
17
+ def check_openpyxl_available():
18
+ """Check if openpyxl is available and return necessary classes.
19
+
20
+ Returns:
21
+ tuple: (Workbook, Font, Alignment, PatternFill, Border, Side) or None if not available
22
+ """
23
+ try:
24
+ from openpyxl import Workbook
25
+ from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
26
+
27
+ return Workbook, Font, Alignment, PatternFill, Border, Side
28
+ except ImportError:
29
+ return None
@@ -0,0 +1,97 @@
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
+ """Form helper functions for planilla views."""
15
+
16
+ from coati_payroll.model import db, TipoPlanilla, Moneda, Empresa, NominaEmpleado, Percepcion, Deduccion
17
+ from coati_payroll.forms import PlanillaForm
18
+ from coati_payroll.i18n import _
19
+ from sqlalchemy.orm import joinedload
20
+
21
+ # Constants
22
+ SELECT_PLACEHOLDER = "-- Seleccionar --"
23
+
24
+
25
+ def populate_form_choices(form: PlanillaForm):
26
+ """Populate form select choices from database."""
27
+ tipos = (
28
+ db.session.execute(db.select(TipoPlanilla).filter_by(activo=True).order_by(TipoPlanilla.codigo)).scalars().all()
29
+ )
30
+ form.tipo_planilla_id.choices = [("", _(SELECT_PLACEHOLDER))] + [
31
+ (t.id, f"{t.codigo} - {t.descripcion or t.codigo}") for t in tipos
32
+ ]
33
+
34
+ monedas = db.session.execute(db.select(Moneda).filter_by(activo=True).order_by(Moneda.codigo)).scalars().all()
35
+ form.moneda_id.choices = [("", _(SELECT_PLACEHOLDER))] + [(m.id, f"{m.codigo} - {m.nombre}") for m in monedas]
36
+
37
+ empresas = (
38
+ db.session.execute(db.select(Empresa).filter_by(activo=True).order_by(Empresa.razon_social)).scalars().all()
39
+ )
40
+ form.empresa_id.choices = [("", _(SELECT_PLACEHOLDER))] + [
41
+ (e.id, f"{e.codigo} - {e.razon_social}") for e in empresas
42
+ ]
43
+
44
+
45
+ def populate_novedad_form_choices(form, nomina_id: str):
46
+ """Populate form select choices for novedad form."""
47
+ # Get employees associated with this nomina with eager loading
48
+ nomina_empleados = (
49
+ db.session.execute(
50
+ db.select(NominaEmpleado).filter_by(nomina_id=nomina_id).options(joinedload(NominaEmpleado.empleado))
51
+ )
52
+ .scalars()
53
+ .all()
54
+ )
55
+ form.empleado_id.choices = [("", _("-- Seleccionar Empleado --"))] + [
56
+ (
57
+ ne.empleado.id,
58
+ f"{ne.empleado.primer_nombre} {ne.empleado.primer_apellido} ({ne.empleado.codigo_empleado})",
59
+ )
60
+ for ne in nomina_empleados
61
+ ]
62
+
63
+ # Get active percepciones
64
+ percepciones = (
65
+ db.session.execute(db.select(Percepcion).filter_by(activo=True).order_by(Percepcion.nombre)).scalars().all()
66
+ )
67
+ form.percepcion_id.choices = [("", _("-- Seleccionar Percepción --"))] + [
68
+ (p.id, f"{p.codigo} - {p.nombre}") for p in percepciones
69
+ ]
70
+
71
+ # Get active deducciones
72
+ deducciones = (
73
+ db.session.execute(db.select(Deduccion).filter_by(activo=True).order_by(Deduccion.nombre)).scalars().all()
74
+ )
75
+ form.deduccion_id.choices = [("", _("-- Seleccionar Deducción --"))] + [
76
+ (d.id, f"{d.codigo} - {d.nombre}") for d in deducciones
77
+ ]
78
+
79
+
80
+ def get_concepto_ids_from_form(form) -> tuple[str | None, str | None]:
81
+ """Extract percepcion_id and deduccion_id from form based on tipo_concepto.
82
+
83
+ Args:
84
+ form: The NominaNovedadForm with submitted data
85
+
86
+ Returns:
87
+ Tuple of (percepcion_id, deduccion_id) - one will be set, other will be None
88
+ """
89
+ percepcion_id = None
90
+ deduccion_id = None
91
+
92
+ if form.tipo_concepto.data == "percepcion":
93
+ percepcion_id = form.percepcion_id.data if form.percepcion_id.data else None
94
+ else:
95
+ deduccion_id = form.deduccion_id.data if form.deduccion_id.data else None
96
+
97
+ return percepcion_id, deduccion_id