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,227 @@
1
+ {% extends 'base.html' %}
2
+
3
+ {% block content %}
4
+ <div class="container-fluid">
5
+ <h2 class="mb-4">{{ _('Panel principal') }}</h2>
6
+
7
+ <!-- Dashboard Cards - Statistics -->
8
+ <div class="row g-3 mb-4">
9
+ <!-- Empleados Card -->
10
+ <div class="col-12 col-sm-6 col-lg-3">
11
+ <div class="card dashboard-card">
12
+ <div class="card-body">
13
+ <div class="d-flex align-items-center">
14
+ <div class="dashboard-icon bg-primary-light">
15
+ <i class="bi bi-people"></i>
16
+ </div>
17
+ <div class="ms-3 flex-grow-1">
18
+ <h6 class="dashboard-card-title">{{ _('Empleados') }}</h6>
19
+ <h3 class="dashboard-card-value">{{ total_empleados }}</h3>
20
+ <p class="dashboard-card-label">{{ _('Activos') }}</p>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+
27
+ <!-- Empresas Card -->
28
+ <div class="col-12 col-sm-6 col-lg-3">
29
+ <div class="card dashboard-card">
30
+ <div class="card-body">
31
+ <div class="d-flex align-items-center">
32
+ <div class="dashboard-icon bg-success-light">
33
+ <i class="bi bi-building"></i>
34
+ </div>
35
+ <div class="ms-3 flex-grow-1">
36
+ <h6 class="dashboard-card-title">{{ _('Empresas') }}</h6>
37
+ <h3 class="dashboard-card-value">{{ total_empresas }}</h3>
38
+ <p class="dashboard-card-label">{{ _('Registradas') }}</p>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <!-- Planillas Card -->
46
+ <div class="col-12 col-sm-6 col-lg-3">
47
+ <div class="card dashboard-card">
48
+ <div class="card-body">
49
+ <div class="d-flex align-items-center">
50
+ <div class="dashboard-icon bg-warning-light">
51
+ <i class="bi bi-calculator"></i>
52
+ </div>
53
+ <div class="ms-3 flex-grow-1">
54
+ <h6 class="dashboard-card-title">{{ _('Planillas') }}</h6>
55
+ <h3 class="dashboard-card-value">{{ total_planillas }}</h3>
56
+ <p class="dashboard-card-label">{{ _('Configuradas') }}</p>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+
63
+ <!-- Nóminas Card -->
64
+ <div class="col-12 col-sm-6 col-lg-3">
65
+ <div class="card dashboard-card">
66
+ <div class="card-body">
67
+ <div class="d-flex align-items-center">
68
+ <div class="dashboard-icon bg-info-light">
69
+ <i class="bi bi-file-earmark-text"></i>
70
+ </div>
71
+ <div class="ms-3 flex-grow-1">
72
+ <h6 class="dashboard-card-title">{{ _('Nóminas') }}</h6>
73
+ <h3 class="dashboard-card-value">{{ total_nominas }}</h3>
74
+ <p class="dashboard-card-label">{{ _('Procesadas') }}</p>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- Quick Actions Section -->
83
+ <div class="row g-3 mb-4">
84
+ <div class="col-12">
85
+ <div class="card">
86
+ <div class="card-header d-flex align-items-center">
87
+ <i class="bi bi-lightning-charge me-2"></i>
88
+ <span>{{ _('Acciones Rápidas') }}</span>
89
+ </div>
90
+ <div class="card-body">
91
+ <div class="row g-3">
92
+ <div class="col-12 col-md-6 col-lg-4">
93
+ <a href="{{ url_for('employee.new') }}" class="quick-action-card">
94
+ <div class="quick-action-icon bg-primary-light">
95
+ <i class="bi bi-person-plus"></i>
96
+ </div>
97
+ <div class="quick-action-content">
98
+ <h6>{{ _('Nuevo Empleado') }}</h6>
99
+ <p>{{ _('Registrar un nuevo empleado en el sistema') }}</p>
100
+ </div>
101
+ </a>
102
+ </div>
103
+
104
+ <div class="col-12 col-md-6 col-lg-4">
105
+ <a href="{{ url_for('planilla.new') }}" class="quick-action-card">
106
+ <div class="quick-action-icon bg-success-light">
107
+ <i class="bi bi-file-earmark-plus"></i>
108
+ </div>
109
+ <div class="quick-action-content">
110
+ <h6>{{ _('Nueva Planilla') }}</h6>
111
+ <p>{{ _('Crear una nueva configuración de planilla') }}</p>
112
+ </div>
113
+ </a>
114
+ </div>
115
+
116
+ <div class="col-12 col-md-6 col-lg-4">
117
+ <a href="{{ url_for('planilla.index') }}" class="quick-action-card">
118
+ <div class="quick-action-icon bg-warning-light">
119
+ <i class="bi bi-play-circle"></i>
120
+ </div>
121
+ <div class="quick-action-content">
122
+ <h6>{{ _('Ejecutar Nómina') }}</h6>
123
+ <p>{{ _('Procesar nómina de una planilla existente') }}</p>
124
+ </div>
125
+ </a>
126
+ </div>
127
+
128
+ <div class="col-12 col-md-6 col-lg-4">
129
+ <a href="{{ url_for('employee.index') }}" class="quick-action-card">
130
+ <div class="quick-action-icon bg-info-light">
131
+ <i class="bi bi-people"></i>
132
+ </div>
133
+ <div class="quick-action-content">
134
+ <h6>{{ _('Ver Empleados') }}</h6>
135
+ <p>{{ _('Consultar listado de empleados') }}</p>
136
+ </div>
137
+ </a>
138
+ </div>
139
+
140
+ <div class="col-12 col-md-6 col-lg-4">
141
+ <a href="{{ url_for('empresa.index') }}" class="quick-action-card">
142
+ <div class="quick-action-icon bg-secondary-light">
143
+ <i class="bi bi-building-gear"></i>
144
+ </div>
145
+ <div class="quick-action-content">
146
+ <h6>{{ _('Gestionar Empresas') }}</h6>
147
+ <p>{{ _('Administrar empresas del sistema') }}</p>
148
+ </div>
149
+ </a>
150
+ </div>
151
+
152
+ <div class="col-12 col-md-6 col-lg-4">
153
+ <a href="{{ url_for('prestamo.index') }}" class="quick-action-card">
154
+ <div class="quick-action-icon bg-danger-light">
155
+ <i class="bi bi-cash-coin"></i>
156
+ </div>
157
+ <div class="quick-action-content">
158
+ <h6>{{ _('Préstamos') }}</h6>
159
+ <p>{{ _('Gestionar préstamos y adelantos') }}</p>
160
+ </div>
161
+ </a>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ </div>
168
+
169
+ <!-- Recent Payrolls Section -->
170
+ {% if recent_nominas %}
171
+ <div class="row g-3">
172
+ <div class="col-12">
173
+ <div class="card">
174
+ <div class="card-header d-flex align-items-center">
175
+ <i class="bi bi-clock-history me-2"></i>
176
+ <span>{{ _('Nóminas Recientes') }}</span>
177
+ </div>
178
+ <div class="card-body">
179
+ <div class="table-responsive">
180
+ <table class="table table-hover table-sm">
181
+ <thead>
182
+ <tr>
183
+ <th>{{ _('Planilla') }}</th>
184
+ <th>{{ _('Período') }}</th>
185
+ <th>{{ _('Estado') }}</th>
186
+ <th>{{ _('Fecha Creación') }}</th>
187
+ <th>{{ _('Acciones') }}</th>
188
+ </tr>
189
+ </thead>
190
+ <tbody>
191
+ {% for nomina in recent_nominas %}
192
+ <tr>
193
+ <td>{{ nomina.planilla.nombre if nomina.planilla else '-' }}</td>
194
+ <td>
195
+ {% if nomina.periodo_inicio and nomina.periodo_fin %}
196
+ {{ nomina.periodo_inicio.strftime('%d/%m/%Y') }} - {{ nomina.periodo_fin.strftime('%d/%m/%Y') }}
197
+ {% else %}
198
+ -
199
+ {% endif %}
200
+ </td>
201
+ <td>
202
+ {% if nomina.estado == 'calculado' or nomina.estado == 'generado' %}
203
+ <span class="badge bg-warning">{{ _('Generada') }}</span>
204
+ {% elif nomina.estado == 'aplicado' %}
205
+ <span class="badge bg-success">{{ _('Aplicada') }}</span>
206
+ {% else %}
207
+ <span class="badge bg-secondary">{{ nomina.estado or '-' }}</span>
208
+ {% endif %}
209
+ </td>
210
+ <td>{{ nomina.fecha_generacion.strftime('%d/%m/%Y %H:%M') if nomina.fecha_generacion else '-' }}</td>
211
+ <td>
212
+ <a href="{{ url_for('planilla.ver_nomina', planilla_id=nomina.planilla_id, nomina_id=nomina.id) }}" class="btn btn-sm btn-outline-primary">
213
+ <i class="bi bi-eye"></i>
214
+ </a>
215
+ </td>
216
+ </tr>
217
+ {% endfor %}
218
+ </tbody>
219
+ </table>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ {% endif %}
226
+ </div>
227
+ {% endblock %}
@@ -0,0 +1,146 @@
1
+ {# Form rendering macros #}
2
+
3
+ {% macro render_field(field, class="form-control") %}
4
+ <div class="mb-3">
5
+ {% if field.type == 'BooleanField' %}
6
+ {# Checkbox with Bootstrap 5 form-check structure #}
7
+ <div class="form-check">
8
+ {{ field(class="form-check-input" + (" is-invalid" if field.errors else "")) }}
9
+ {{ field.label(class="form-check-label") }}
10
+ {% if field.errors %}
11
+ {% for error in field.errors %}
12
+ <div class="invalid-feedback d-block">{{ error }}</div>
13
+ {% endfor %}
14
+ {% endif %}
15
+ </div>
16
+ {% elif field.type == 'RadioField' %}
17
+ {# Radio buttons with Bootstrap 5 form-check structure #}
18
+ {{ field.label(class="form-label") }}
19
+ {% for subfield in field %}
20
+ <div class="form-check">
21
+ {{ subfield(class="form-check-input" + (" is-invalid" if field.errors else "")) }}
22
+ {{ subfield.label(class="form-check-label") }}
23
+ </div>
24
+ {% endfor %}
25
+ {% if field.errors %}
26
+ {% for error in field.errors %}
27
+ <div class="invalid-feedback d-block">{{ error }}</div>
28
+ {% endfor %}
29
+ {% endif %}
30
+ {% elif field.type == 'SelectField' or field.type == 'SelectMultipleField' %}
31
+ {# Select dropdowns with Bootstrap 5 form-select #}
32
+ {{ field.label(class="form-label") }}
33
+ {{ field(class="form-select" + (" is-invalid" if field.errors else "")) }}
34
+ {% if field.errors %}
35
+ {% for error in field.errors %}
36
+ <div class="invalid-feedback d-block">{{ error }}</div>
37
+ {% endfor %}
38
+ {% endif %}
39
+ {% else %}
40
+ {# Standard inputs (text, email, password, number, date, etc.) with form-control #}
41
+ {{ field.label(class="form-label") }}
42
+ {{ field(class="form-control" + (" is-invalid" if field.errors else "")) }}
43
+ {% if field.errors %}
44
+ {% for error in field.errors %}
45
+ <div class="invalid-feedback d-block">{{ error }}</div>
46
+ {% endfor %}
47
+ {% endif %}
48
+ {% endif %}
49
+ </div>
50
+ {% endmacro %}
51
+
52
+ {% macro render_messages() %}
53
+ {% with messages = get_flashed_messages(with_categories=true) %}
54
+ {% if messages %}
55
+ {% for category, message in messages %}
56
+ <div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
57
+ {{ message }}
58
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
59
+ </div>
60
+ {% endfor %}
61
+ {% endif %}
62
+ {% endwith %}
63
+ {% endmacro %}
64
+
65
+ {% macro render_form_errors(form) %}
66
+ {% if form.errors %}
67
+ <div class="alert alert-danger alert-dismissible fade show" role="alert">
68
+ <strong>{{ _('Por favor corrija los siguientes errores:') }}</strong>
69
+ <ul class="mb-0 mt-2">
70
+ {% for field_name, errors in form.errors.items() %}
71
+ {% for error in errors %}
72
+ <li>{{ form[field_name].label.text }}: {{ error }}</li>
73
+ {% endfor %}
74
+ {% endfor %}
75
+ </ul>
76
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
77
+ </div>
78
+ {% endif %}
79
+ {% endmacro %}
80
+
81
+ {% macro render_pagination(pagination, endpoint) %}
82
+ {% if pagination.pages > 1 %}
83
+ {# Create a dict of query args excluding 'page' to avoid duplicate keyword argument #}
84
+ {% set query_args = request.args.to_dict(flat=False) %}
85
+ {% if query_args.pop('page', None) %}{% endif %}
86
+ <nav aria-label="{{ _('Navegación de páginas') }}" class="mt-4">
87
+ <div class="d-flex justify-content-between align-items-center">
88
+ <small class="text-muted">
89
+ {{ _('Mostrando') }} {{ ((pagination.page - 1) * pagination.per_page) + 1 }} -
90
+ {{ [pagination.page * pagination.per_page, pagination.total]|min }}
91
+ {{ _('de') }} {{ pagination.total }} {{ _('registros') }}
92
+ </small>
93
+ <ul class="pagination pagination-sm mb-0">
94
+ {# Previous page #}
95
+ {% if pagination.has_prev %}
96
+ <li class="page-item">
97
+ <a class="page-link" href="{{ url_for(endpoint, page=pagination.prev_num, **query_args) }}" aria-label="{{ _('Anterior') }}">
98
+ <span aria-hidden="true">&laquo;</span>
99
+ </a>
100
+ </li>
101
+ {% else %}
102
+ <li class="page-item disabled">
103
+ <span class="page-link" aria-label="{{ _('Anterior') }}">
104
+ <span aria-hidden="true">&laquo;</span>
105
+ </span>
106
+ </li>
107
+ {% endif %}
108
+
109
+ {# Page numbers #}
110
+ {% for page_num in pagination.iter_pages(left_edge=1, left_current=2, right_current=2, right_edge=1) %}
111
+ {% if page_num %}
112
+ {% if page_num == pagination.page %}
113
+ <li class="page-item active" aria-current="page">
114
+ <span class="page-link">{{ page_num }}</span>
115
+ </li>
116
+ {% else %}
117
+ <li class="page-item">
118
+ <a class="page-link" href="{{ url_for(endpoint, page=page_num, **query_args) }}">{{ page_num }}</a>
119
+ </li>
120
+ {% endif %}
121
+ {% else %}
122
+ <li class="page-item disabled">
123
+ <span class="page-link">…</span>
124
+ </li>
125
+ {% endif %}
126
+ {% endfor %}
127
+
128
+ {# Next page #}
129
+ {% if pagination.has_next %}
130
+ <li class="page-item">
131
+ <a class="page-link" href="{{ url_for(endpoint, page=pagination.next_num, **query_args) }}" aria-label="{{ _('Siguiente') }}">
132
+ <span aria-hidden="true">&raquo;</span>
133
+ </a>
134
+ </li>
135
+ {% else %}
136
+ <li class="page-item disabled">
137
+ <span class="page-link" aria-label="{{ _('Siguiente') }}">
138
+ <span aria-hidden="true">&raquo;</span>
139
+ </span>
140
+ </li>
141
+ {% endif %}
142
+ </ul>
143
+ </div>
144
+ </nav>
145
+ {% endif %}
146
+ {% endmacro %}
@@ -0,0 +1,78 @@
1
+ {% extends 'base.html' %}
2
+ {% from 'macros.html' import render_field, render_messages, render_form_errors %}
3
+
4
+ {% block content %}
5
+ <div class="container">
6
+ {{ render_messages() }}
7
+ {{ render_form_errors(form) }}
8
+
9
+ <div class="d-flex justify-content-between align-items-center mb-4">
10
+ <h2>{{ title }}</h2>
11
+ <a href="{{ url_for('calculation_rule.index') }}" class="btn btn-secondary">
12
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
13
+ </a>
14
+ </div>
15
+
16
+ <div class="card">
17
+ <div class="card-body">
18
+ <form method="POST" action="">
19
+ {{ form.hidden_tag() }}
20
+
21
+ <div class="row">
22
+ <div class="col-md-4">
23
+ {{ render_field(form.codigo) }}
24
+ </div>
25
+ <div class="col-md-4">
26
+ {{ render_field(form.nombre) }}
27
+ </div>
28
+ <div class="col-md-4">
29
+ {{ render_field(form.tipo_regla) }}
30
+ </div>
31
+ </div>
32
+
33
+ <div class="row">
34
+ <div class="col-md-12">
35
+ {{ render_field(form.descripcion) }}
36
+ </div>
37
+ </div>
38
+
39
+ <div class="row">
40
+ <div class="col-md-4">
41
+ {{ render_field(form.jurisdiccion) }}
42
+ </div>
43
+ <div class="col-md-4">
44
+ {{ render_field(form.moneda_referencia) }}
45
+ </div>
46
+ <div class="col-md-4">
47
+ {{ render_field(form.version) }}
48
+ </div>
49
+ </div>
50
+
51
+ <div class="row">
52
+ <div class="col-md-4">
53
+ {{ render_field(form.vigente_desde) }}
54
+ </div>
55
+ <div class="col-md-4">
56
+ {{ render_field(form.vigente_hasta) }}
57
+ </div>
58
+ <div class="col-md-4">
59
+ <div class="mb-3 mt-4">
60
+ {{ render_field(form.activo) }}
61
+ </div>
62
+ </div>
63
+ </div>
64
+
65
+ <div class="mt-4">
66
+ {{ form.submit(class="btn btn-primary") }}
67
+ <a href="{{ url_for('calculation_rule.index') }}" class="btn btn-secondary">{{ _('Cancelar') }}</a>
68
+ {% if rule %}
69
+ <a href="{{ url_for('calculation_rule.edit_schema', id=rule.id) }}" class="btn btn-info">
70
+ <i class="bi bi-code-square me-1"></i>{{ _('Editar Esquema') }}
71
+ </a>
72
+ {% endif %}
73
+ </div>
74
+ </form>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ {% endblock %}
@@ -0,0 +1,102 @@
1
+ {% extends 'base.html' %}
2
+ {% from 'macros.html' import render_messages, render_pagination %}
3
+
4
+ {% block content %}
5
+ <div class="container">
6
+ {{ render_messages() }}
7
+
8
+ <div class="d-flex justify-content-between align-items-center mb-4">
9
+ <h2>{{ _('Reglas de Cálculo') }}</h2>
10
+ <a href="{{ url_for('calculation_rule.new') }}" class="btn btn-primary">
11
+ <i class="bi bi-plus-lg me-1"></i>{{ _('Nueva Regla') }}
12
+ </a>
13
+ </div>
14
+
15
+ <div class="card">
16
+ <div class="card-body">
17
+ <div class="table-responsive">
18
+ <table class="table table-hover">
19
+ <thead>
20
+ <tr>
21
+ <th>{{ _('Código') }}</th>
22
+ <th>{{ _('Nombre') }}</th>
23
+ <th>{{ _('Tipo') }}</th>
24
+ <th>{{ _('Jurisdicción') }}</th>
25
+ <th>{{ _('Versión') }}</th>
26
+ <th>{{ _('Vigencia') }}</th>
27
+ <th>{{ _('Estado') }}</th>
28
+ <th>{{ _('Acciones') }}</th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ {% for rule in rules %}
33
+ <tr>
34
+ <td><strong>{{ rule.codigo }}</strong></td>
35
+ <td>{{ rule.nombre }}</td>
36
+ <td>
37
+ {% if rule.tipo_regla == 'impuesto' %}
38
+ <span class="badge bg-danger">{{ _('Impuesto') }}</span>
39
+ {% elif rule.tipo_regla == 'deduccion' %}
40
+ <span class="badge bg-warning text-dark">{{ _('Deducción') }}</span>
41
+ {% elif rule.tipo_regla == 'percepcion' %}
42
+ <span class="badge bg-info">{{ _('Percepción') }}</span>
43
+ {% else %}
44
+ <span class="badge bg-primary">{{ _('Prestación') }}</span>
45
+ {% endif %}
46
+ </td>
47
+ <td>{{ rule.jurisdiccion or '-' }}</td>
48
+ <td><code>{{ rule.version }}</code></td>
49
+ <td>
50
+ {{ rule.vigente_desde.strftime('%d/%m/%Y') if rule.vigente_desde else '-' }}
51
+ {% if rule.vigente_hasta %}
52
+ - {{ rule.vigente_hasta.strftime('%d/%m/%Y') }}
53
+ {% endif %}
54
+ </td>
55
+ <td>
56
+ {% if rule.activo %}
57
+ <span class="badge bg-success">{{ _('Activo') }}</span>
58
+ {% else %}
59
+ <span class="badge bg-secondary">{{ _('Inactivo') }}</span>
60
+ {% endif %}
61
+ </td>
62
+ <td>
63
+ <div class="btn-group btn-group-sm">
64
+ <a href="{{ url_for('calculation_rule.edit_schema', id=rule.id) }}"
65
+ class="btn btn-outline-primary" title="{{ _('Editar Esquema') }}">
66
+ <i class="bi bi-code-square"></i>
67
+ </a>
68
+ <a href="{{ url_for('calculation_rule.edit', id=rule.id) }}"
69
+ class="btn btn-outline-secondary" title="{{ _('Editar Metadatos') }}">
70
+ <i class="bi bi-pencil"></i>
71
+ </a>
72
+ <form action="{{ url_for('calculation_rule.duplicate', id=rule.id) }}"
73
+ method="POST" class="d-inline">
74
+ <button type="submit" class="btn btn-outline-info" title="{{ _('Duplicar') }}">
75
+ <i class="bi bi-copy"></i>
76
+ </button>
77
+ </form>
78
+ <form action="{{ url_for('calculation_rule.delete', id=rule.id) }}"
79
+ method="POST" class="d-inline"
80
+ onsubmit="return confirm('{{ _('¿Está seguro de eliminar esta regla?') }}');">
81
+ <button type="submit" class="btn btn-outline-danger" title="{{ _('Eliminar') }}">
82
+ <i class="bi bi-trash"></i>
83
+ </button>
84
+ </form>
85
+ </div>
86
+ </td>
87
+ </tr>
88
+ {% else %}
89
+ <tr>
90
+ <td colspan="8" class="text-center text-muted">
91
+ {{ _('No hay reglas de cálculo registradas.') }}
92
+ </td>
93
+ </tr>
94
+ {% endfor %}
95
+ </tbody>
96
+ </table>
97
+ </div>
98
+ {{ render_pagination(pagination, 'calculation_rule.index') }}
99
+ </div>
100
+ </div>
101
+ </div>
102
+ {% endblock %}