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,125 @@
1
+ {% extends "base.html" %}
2
+ {% from "macros.html" import render_pagination %}
3
+
4
+ {% block title %}{{ _('Administración de Reportes') }}{% endblock %}
5
+
6
+ {% block content %}
7
+ <div class="container-fluid">
8
+ <div class="d-flex justify-content-between align-items-center mb-4">
9
+ <h1 class="h2"><i class="bi bi-gear me-2"></i>{{ _('Administración de Reportes') }}</h1>
10
+ <a href="{{ url_for('report.index') }}" class="btn btn-outline-secondary">
11
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver a Reportes') }}
12
+ </a>
13
+ </div>
14
+
15
+ <!-- Filters -->
16
+ <div class="card mb-4">
17
+ <div class="card-body">
18
+ <form method="get" class="row g-3">
19
+ <div class="col-md-3">
20
+ <label for="category" class="form-label">{{ _('Categoría') }}</label>
21
+ <select name="category" id="category" class="form-select">
22
+ <option value="">{{ _('Todas') }}</option>
23
+ {% for cat in categories %}
24
+ <option value="{{ cat }}" {% if cat == current_category %}selected{% endif %}>
25
+ {{ cat|capitalize }}
26
+ </option>
27
+ {% endfor %}
28
+ </select>
29
+ </div>
30
+ <div class="col-md-3">
31
+ <label for="type" class="form-label">{{ _('Tipo') }}</label>
32
+ <select name="type" id="type" class="form-select">
33
+ <option value="">{{ _('Todos') }}</option>
34
+ <option value="system" {% if current_type == 'system' %}selected{% endif %}>{{ _('Sistema') }}</option>
35
+ <option value="custom" {% if current_type == 'custom' %}selected{% endif %}>{{ _('Personalizado') }}</option>
36
+ </select>
37
+ </div>
38
+ <div class="col-md-3">
39
+ <label for="status" class="form-label">{{ _('Estado') }}</label>
40
+ <select name="status" id="status" class="form-select">
41
+ <option value="">{{ _('Todos') }}</option>
42
+ <option value="enabled" {% if current_status == 'enabled' %}selected{% endif %}>{{ _('Habilitado') }}</option>
43
+ <option value="disabled" {% if current_status == 'disabled' %}selected{% endif %}>{{ _('Deshabilitado') }}</option>
44
+ </select>
45
+ </div>
46
+ <div class="col-md-3 d-flex align-items-end">
47
+ <button type="submit" class="btn btn-primary">
48
+ <i class="bi bi-funnel me-1"></i>{{ _('Filtrar') }}
49
+ </button>
50
+ <a href="{{ url_for('report.admin_index') }}" class="btn btn-outline-secondary ms-2">
51
+ <i class="bi bi-x-circle me-1"></i>{{ _('Limpiar') }}
52
+ </a>
53
+ </div>
54
+ </form>
55
+ </div>
56
+ </div>
57
+
58
+ <!-- Reports Table -->
59
+ <div class="card">
60
+ <div class="card-body">
61
+ <div class="table-responsive">
62
+ <table class="table table-hover">
63
+ <thead>
64
+ <tr>
65
+ <th>{{ _('Nombre') }}</th>
66
+ <th>{{ _('Tipo') }}</th>
67
+ <th>{{ _('Categoría') }}</th>
68
+ <th>{{ _('Base') }}</th>
69
+ <th>{{ _('Estado') }}</th>
70
+ <th>{{ _('Acciones') }}</th>
71
+ </tr>
72
+ </thead>
73
+ <tbody>
74
+ {% for report in reports %}
75
+ <tr>
76
+ <td>
77
+ <strong>{{ report.name }}</strong>
78
+ <br>
79
+ <small class="text-muted">{{ report.description or _('Sin descripción') }}</small>
80
+ </td>
81
+ <td>
82
+ <span class="badge bg-{{ 'primary' if report.type == 'system' else 'info' }}">
83
+ {{ _('Sistema') if report.type == 'system' else _('Personalizado') }}
84
+ </span>
85
+ </td>
86
+ <td>{{ report.category|capitalize if report.category else '-' }}</td>
87
+ <td>{{ report.base_entity }}</td>
88
+ <td>
89
+ <span class="badge bg-{{ 'success' if report.status == 'enabled' else 'secondary' }}">
90
+ {{ _('Habilitado') if report.status == 'enabled' else _('Deshabilitado') }}
91
+ </span>
92
+ </td>
93
+ <td>
94
+ <div class="btn-group btn-group-sm" role="group">
95
+ <a href="{{ url_for('report.detail', report_id=report.id) }}" class="btn btn-outline-primary" title="{{ _('Ver detalles') }}">
96
+ <i class="bi bi-eye"></i>
97
+ </a>
98
+ <a href="{{ url_for('report.permissions_form', report_id=report.id) }}" class="btn btn-outline-secondary" title="{{ _('Permisos') }}">
99
+ <i class="bi bi-shield-lock"></i>
100
+ </a>
101
+ <form method="post" action="{{ url_for('report.toggle_status', report_id=report.id) }}" class="d-inline">
102
+ <button type="submit" class="btn btn-outline-{{ 'danger' if report.status == 'enabled' else 'success' }}" title="{{ _('Cambiar estado') }}">
103
+ <i class="bi bi-{{ 'toggle-off' if report.status == 'enabled' else 'toggle-on' }}"></i>
104
+ </button>
105
+ </form>
106
+ </div>
107
+ </td>
108
+ </tr>
109
+ {% else %}
110
+ <tr>
111
+ <td colspan="6" class="text-center text-muted">
112
+ {{ _('No hay reportes disponibles.') }}
113
+ </td>
114
+ </tr>
115
+ {% endfor %}
116
+ </tbody>
117
+ </table>
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ <!-- Pagination -->
123
+ {{ render_pagination(pagination, 'report.admin_index') }}
124
+ </div>
125
+ {% endblock %}
@@ -0,0 +1,129 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ report.name }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1 class="h2"><i class="bi bi-file-earmark-text me-2"></i>{{ report.name }}</h1>
9
+ <div>
10
+ <a href="{{ url_for('report.execute_form', report_id=report.id) }}" class="btn btn-primary">
11
+ <i class="bi bi-play-fill me-1"></i>{{ _('Ejecutar') }}
12
+ </a>
13
+ <a href="{{ url_for('report.index') }}" class="btn btn-outline-secondary">
14
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
15
+ </a>
16
+ </div>
17
+ </div>
18
+
19
+ <div class="row">
20
+ <div class="col-lg-8">
21
+ <!-- Report Info -->
22
+ <div class="card mb-4">
23
+ <div class="card-header">
24
+ <h5 class="mb-0">{{ _('Información del Reporte') }}</h5>
25
+ </div>
26
+ <div class="card-body">
27
+ <dl class="row">
28
+ <dt class="col-sm-3">{{ _('Nombre') }}:</dt>
29
+ <dd class="col-sm-9">{{ report.name }}</dd>
30
+
31
+ <dt class="col-sm-3">{{ _('Descripción') }}:</dt>
32
+ <dd class="col-sm-9">{{ report.description or _('Sin descripción') }}</dd>
33
+
34
+ <dt class="col-sm-3">{{ _('Tipo') }}:</dt>
35
+ <dd class="col-sm-9">
36
+ <span class="badge bg-{{ 'primary' if report.type == 'system' else 'info' }}">
37
+ {{ _('Sistema') if report.type == 'system' else _('Personalizado') }}
38
+ </span>
39
+ </dd>
40
+
41
+ <dt class="col-sm-3">{{ _('Categoría') }}:</dt>
42
+ <dd class="col-sm-9">{{ report.category|capitalize if report.category else '-' }}</dd>
43
+
44
+ <dt class="col-sm-3">{{ _('Entidad Base') }}:</dt>
45
+ <dd class="col-sm-9">{{ report.base_entity }}</dd>
46
+
47
+ <dt class="col-sm-3">{{ _('Estado') }}:</dt>
48
+ <dd class="col-sm-9">
49
+ <span class="badge bg-{{ 'success' if report.status == 'enabled' else 'secondary' }}">
50
+ {{ _('Habilitado') if report.status == 'enabled' else _('Deshabilitado') }}
51
+ </span>
52
+ </dd>
53
+ </dl>
54
+ </div>
55
+ </div>
56
+
57
+ <!-- Recent Executions -->
58
+ <div class="card">
59
+ <div class="card-header">
60
+ <h5 class="mb-0">{{ _('Ejecuciones Recientes') }}</h5>
61
+ </div>
62
+ <div class="card-body">
63
+ {% if executions %}
64
+ <div class="table-responsive">
65
+ <table class="table table-sm">
66
+ <thead>
67
+ <tr>
68
+ <th>{{ _('Fecha') }}</th>
69
+ <th>{{ _('Usuario') }}</th>
70
+ <th>{{ _('Estado') }}</th>
71
+ <th>{{ _('Registros') }}</th>
72
+ <th>{{ _('Tiempo') }}</th>
73
+ </tr>
74
+ </thead>
75
+ <tbody>
76
+ {% for exec in executions %}
77
+ <tr>
78
+ <td>{{ exec.timestamp.strftime('%Y-%m-%d %H:%M') if exec.timestamp else exec.creado.strftime('%Y-%m-%d') }}</td>
79
+ <td>{{ exec.executed_by }}</td>
80
+ <td>
81
+ <span class="badge bg-{{ 'success' if exec.status == 'completed' else 'danger' if exec.status == 'failed' else 'warning' }}">
82
+ {{ exec.status }}
83
+ </span>
84
+ </td>
85
+ <td>{{ exec.row_count or '-' }}</td>
86
+ <td>{{ exec.execution_time_ms or '-' }} ms</td>
87
+ </tr>
88
+ {% endfor %}
89
+ </tbody>
90
+ </table>
91
+ </div>
92
+ {% else %}
93
+ <p class="text-muted">{{ _('No hay ejecuciones registradas.') }}</p>
94
+ {% endif %}
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="col-lg-4">
100
+ {% if metadata %}
101
+ <!-- Parameters Info -->
102
+ <div class="card mb-3">
103
+ <div class="card-header">
104
+ <h5 class="mb-0">{{ _('Parámetros') }}</h5>
105
+ </div>
106
+ <div class="card-body">
107
+ {% if metadata.parameters %}
108
+ <ul class="list-unstyled mb-0">
109
+ {% for param in metadata.parameters %}
110
+ <li class="mb-2">
111
+ <strong>{{ param.name|replace('_', ' ')|title }}</strong>
112
+ <br>
113
+ <small class="text-muted">
114
+ {{ _('Tipo') }}: {{ param.type }}
115
+ {% if param.required %}<span class="text-danger">({{ _('Requerido') }})</span>{% endif %}
116
+ </small>
117
+ </li>
118
+ {% endfor %}
119
+ </ul>
120
+ {% else %}
121
+ <p class="text-muted mb-0">{{ _('Sin parámetros') }}</p>
122
+ {% endif %}
123
+ </div>
124
+ </div>
125
+ {% endif %}
126
+ </div>
127
+ </div>
128
+ </div>
129
+ {% endblock %}
@@ -0,0 +1,266 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ _('Ejecutar Reporte') }} - {{ report.name }}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="container-fluid">
7
+ <div class="d-flex justify-content-between align-items-center mb-4">
8
+ <h1 class="h2">
9
+ <i class="bi bi-play-circle me-2"></i>{{ _('Ejecutar Reporte') }}: {{ report.name }}
10
+ </h1>
11
+ <a href="{{ url_for('report.index') }}" class="btn btn-outline-secondary">
12
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
13
+ </a>
14
+ </div>
15
+
16
+ <div class="row">
17
+ <div class="col-lg-8">
18
+ <!-- Report Info -->
19
+ <div class="card mb-4">
20
+ <div class="card-body">
21
+ <h5 class="card-title">{{ report.name }}</h5>
22
+ <p class="card-text">{{ report.description or _('Sin descripción') }}</p>
23
+ <div class="row">
24
+ <div class="col-md-6">
25
+ <small class="text-muted">
26
+ <strong>{{ _('Tipo') }}:</strong>
27
+ <span class="badge bg-{{ 'primary' if report.type == 'system' else 'info' }} ms-1">
28
+ {{ _('Sistema') if report.type == 'system' else _('Personalizado') }}
29
+ </span>
30
+ </small>
31
+ </div>
32
+ <div class="col-md-6">
33
+ <small class="text-muted">
34
+ <strong>{{ _('Categoría') }}:</strong> {{ report.category|capitalize if report.category else '-' }}
35
+ </small>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Parameters Form -->
42
+ <div class="card mb-4">
43
+ <div class="card-header">
44
+ <h5 class="mb-0">{{ _('Parámetros del Reporte') }}</h5>
45
+ </div>
46
+ <div class="card-body">
47
+ <form id="reportForm">
48
+ {% if metadata and metadata.parameters %}
49
+ {% for param in metadata.parameters %}
50
+ <div class="mb-3">
51
+ <label for="param_{{ param.name }}" class="form-label">
52
+ {{ param.name|replace('_', ' ')|title }}
53
+ {% if param.required %}<span class="text-danger">*</span>{% endif %}
54
+ </label>
55
+ {% if param.type == 'date' %}
56
+ <input type="date" class="form-control" id="param_{{ param.name }}" name="{{ param.name }}" {% if param.required %}required{% endif %}>
57
+ {% elif param.type == 'boolean' %}
58
+ <select class="form-select" id="param_{{ param.name }}" name="{{ param.name }}">
59
+ <option value="">{{ _('Seleccionar...') }}</option>
60
+ <option value="true">{{ _('Sí') }}</option>
61
+ <option value="false">{{ _('No') }}</option>
62
+ </select>
63
+ {% else %}
64
+ <input type="text" class="form-control" id="param_{{ param.name }}" name="{{ param.name }}" {% if param.required %}required{% endif %}>
65
+ {% endif %}
66
+ </div>
67
+ {% endfor %}
68
+ {% else %}
69
+ <p class="text-muted">{{ _('Este reporte no requiere parámetros.') }}</p>
70
+ {% endif %}
71
+
72
+ <div class="d-grid gap-2 d-md-flex justify-content-md-end">
73
+ <button type="submit" class="btn btn-primary" id="executeBtn">
74
+ <i class="bi bi-play-fill me-1"></i>{{ _('Ejecutar Reporte') }}
75
+ </button>
76
+ <button type="button" class="btn btn-success" id="exportBtn" style="display:none;">
77
+ <i class="bi bi-file-earmark-spreadsheet me-1"></i>{{ _('Exportar a Excel') }}
78
+ </button>
79
+ </div>
80
+ </form>
81
+ </div>
82
+ </div>
83
+
84
+ <!-- Results -->
85
+ <div id="resultsContainer" style="display:none;">
86
+ <div class="card">
87
+ <div class="card-header d-flex justify-content-between align-items-center">
88
+ <h5 class="mb-0">{{ _('Resultados') }}</h5>
89
+ <span id="resultCount" class="badge bg-primary"></span>
90
+ </div>
91
+ <div class="card-body">
92
+ <div id="executionInfo" class="alert alert-info mb-3" style="display:none;">
93
+ <small>
94
+ <i class="bi bi-clock me-1"></i>
95
+ {{ _('Tiempo de ejecución') }}: <span id="executionTime"></span> ms
96
+ </small>
97
+ </div>
98
+ <div class="table-responsive">
99
+ <table class="table table-striped table-hover" id="resultsTable">
100
+ <thead id="resultsTableHead"></thead>
101
+ <tbody id="resultsTableBody"></tbody>
102
+ </table>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <!-- Loading Spinner -->
109
+ <div id="loadingSpinner" class="text-center my-5" style="display:none;">
110
+ <div class="spinner-border text-primary" role="status">
111
+ <span class="visually-hidden">{{ _('Cargando...') }}</span>
112
+ </div>
113
+ <p class="mt-2">{{ _('Ejecutando reporte...') }}</p>
114
+ </div>
115
+
116
+ <!-- Error Alert -->
117
+ <div id="errorAlert" class="alert alert-danger" style="display:none;">
118
+ <i class="bi bi-exclamation-triangle me-2"></i>
119
+ <span id="errorMessage"></span>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="col-lg-4">
124
+ <!-- Help Card -->
125
+ <div class="card">
126
+ <div class="card-header">
127
+ <h5 class="mb-0"><i class="bi bi-question-circle me-2"></i>{{ _('Ayuda') }}</h5>
128
+ </div>
129
+ <div class="card-body">
130
+ <p class="card-text small">
131
+ {{ _('Complete los parámetros requeridos y haga clic en "Ejecutar Reporte" para ver los resultados.') }}
132
+ </p>
133
+ <p class="card-text small">
134
+ {{ _('Una vez generado el reporte, podrá exportar los resultados a Excel.') }}
135
+ </p>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+
142
+ <script>
143
+ document.addEventListener('DOMContentLoaded', function() {
144
+ const form = document.getElementById('reportForm');
145
+ const executeBtn = document.getElementById('executeBtn');
146
+ const exportBtn = document.getElementById('exportBtn');
147
+ const loadingSpinner = document.getElementById('loadingSpinner');
148
+ const resultsContainer = document.getElementById('resultsContainer');
149
+ const errorAlert = document.getElementById('errorAlert');
150
+ let currentParameters = {};
151
+
152
+ form.addEventListener('submit', function(e) {
153
+ e.preventDefault();
154
+ executeReport();
155
+ });
156
+
157
+ exportBtn.addEventListener('click', function() {
158
+ exportReport();
159
+ });
160
+
161
+ function executeReport() {
162
+ // Collect parameters
163
+ const formData = new FormData(form);
164
+ const parameters = {};
165
+ for (let [key, value] of formData.entries()) {
166
+ if (value) {
167
+ parameters[key] = value;
168
+ }
169
+ }
170
+ currentParameters = parameters;
171
+
172
+ // Show loading
173
+ loadingSpinner.style.display = 'block';
174
+ resultsContainer.style.display = 'none';
175
+ errorAlert.style.display = 'none';
176
+ exportBtn.style.display = 'none';
177
+
178
+ // Execute report
179
+ fetch('{{ url_for("report.run_report", report_id=report.id) }}', {
180
+ method: 'POST',
181
+ headers: {
182
+ 'Content-Type': 'application/json',
183
+ },
184
+ body: JSON.stringify(parameters)
185
+ })
186
+ .then(response => response.json())
187
+ .then(data => {
188
+ loadingSpinner.style.display = 'none';
189
+
190
+ if (data.success) {
191
+ displayResults(data.results, data.total_count, data.execution_time_ms);
192
+ exportBtn.style.display = 'inline-block';
193
+ } else {
194
+ showError(data.error || '{{ _("Error al ejecutar el reporte") }}');
195
+ }
196
+ })
197
+ .catch(error => {
198
+ loadingSpinner.style.display = 'none';
199
+ showError('{{ _("Error de conexión") }}: ' + error.message);
200
+ });
201
+ }
202
+
203
+ function displayResults(results, totalCount, executionTime) {
204
+ if (!results || results.length === 0) {
205
+ showError('{{ _("El reporte no generó resultados.") }}');
206
+ return;
207
+ }
208
+
209
+ // Update result count
210
+ document.getElementById('resultCount').textContent = totalCount + ' {{ _("registros") }}';
211
+ document.getElementById('executionTime').textContent = executionTime;
212
+ document.getElementById('executionInfo').style.display = 'block';
213
+
214
+ // Build table header
215
+ const headers = Object.keys(results[0]);
216
+ const thead = document.getElementById('resultsTableHead');
217
+ thead.innerHTML = '<tr>' + headers.map(h => '<th>' + h + '</th>').join('') + '</tr>';
218
+
219
+ // Build table body
220
+ const tbody = document.getElementById('resultsTableBody');
221
+ tbody.innerHTML = results.map(row => {
222
+ return '<tr>' + headers.map(h => '<td>' + (row[h] !== null && row[h] !== undefined ? row[h] : '-') + '</td>').join('') + '</tr>';
223
+ }).join('');
224
+
225
+ // Show results
226
+ resultsContainer.style.display = 'block';
227
+ }
228
+
229
+ function showError(message) {
230
+ document.getElementById('errorMessage').textContent = message;
231
+ errorAlert.style.display = 'block';
232
+ }
233
+
234
+ function exportReport() {
235
+ // Export to Excel
236
+ fetch('{{ url_for("report.export_report", report_id=report.id, format="excel") }}', {
237
+ method: 'POST',
238
+ headers: {
239
+ 'Content-Type': 'application/json',
240
+ },
241
+ body: JSON.stringify(currentParameters)
242
+ })
243
+ .then(response => {
244
+ if (response.ok) {
245
+ return response.blob();
246
+ }
247
+ throw new Error('Export failed');
248
+ })
249
+ .then(blob => {
250
+ // Download file
251
+ const url = window.URL.createObjectURL(blob);
252
+ const a = document.createElement('a');
253
+ a.href = url;
254
+ a.download = '{{ report.name }}.xlsx';
255
+ document.body.appendChild(a);
256
+ a.click();
257
+ window.URL.revokeObjectURL(url);
258
+ document.body.removeChild(a);
259
+ })
260
+ .catch(error => {
261
+ showError('{{ _("Error al exportar") }}: ' + error.message);
262
+ });
263
+ }
264
+ });
265
+ </script>
266
+ {% endblock %}
@@ -0,0 +1,95 @@
1
+ {% extends "base.html" %}
2
+ {% from "macros.html" import render_pagination %}
3
+
4
+ {% block title %}{{ _('Reportes') }}{% endblock %}
5
+
6
+ {% block content %}
7
+ <div class="container-fluid">
8
+ <div class="d-flex justify-content-between align-items-center mb-4">
9
+ <h1 class="h2"><i class="bi bi-file-earmark-bar-graph me-2"></i>{{ _('Reportes') }}</h1>
10
+ {% if current_user.tipo == 'admin' %}
11
+ <a href="{{ url_for('report.admin_index') }}" class="btn btn-secondary">
12
+ <i class="bi bi-gear me-1"></i>{{ _('Administración') }}
13
+ </a>
14
+ {% endif %}
15
+ </div>
16
+
17
+ <!-- Filters -->
18
+ <div class="card mb-4">
19
+ <div class="card-body">
20
+ <form method="get" class="row g-3">
21
+ <div class="col-md-4">
22
+ <label for="category" class="form-label">{{ _('Categoría') }}</label>
23
+ <select name="category" id="category" class="form-select">
24
+ <option value="">{{ _('Todas') }}</option>
25
+ {% for cat in categories %}
26
+ <option value="{{ cat }}" {% if cat == current_category %}selected{% endif %}>
27
+ {{ cat|capitalize }}
28
+ </option>
29
+ {% endfor %}
30
+ </select>
31
+ </div>
32
+ <div class="col-md-4">
33
+ <label for="type" class="form-label">{{ _('Tipo') }}</label>
34
+ <select name="type" id="type" class="form-select">
35
+ <option value="">{{ _('Todos') }}</option>
36
+ <option value="system" {% if current_type == 'system' %}selected{% endif %}>{{ _('Sistema') }}</option>
37
+ <option value="custom" {% if current_type == 'custom' %}selected{% endif %}>{{ _('Personalizado') }}</option>
38
+ </select>
39
+ </div>
40
+ <div class="col-md-4 d-flex align-items-end">
41
+ <button type="submit" class="btn btn-primary">
42
+ <i class="bi bi-funnel me-1"></i>{{ _('Filtrar') }}
43
+ </button>
44
+ <a href="{{ url_for('report.index') }}" class="btn btn-outline-secondary ms-2">
45
+ <i class="bi bi-x-circle me-1"></i>{{ _('Limpiar') }}
46
+ </a>
47
+ </div>
48
+ </form>
49
+ </div>
50
+ </div>
51
+
52
+ <!-- Reports List -->
53
+ <div class="row">
54
+ {% for report in reports %}
55
+ <div class="col-md-6 col-lg-4 mb-4">
56
+ <div class="card h-100">
57
+ <div class="card-body">
58
+ <div class="d-flex justify-content-between align-items-start mb-2">
59
+ <h5 class="card-title mb-0">{{ report.name }}</h5>
60
+ <span class="badge bg-{{ 'primary' if report.type == 'system' else 'info' }}">
61
+ {{ _('Sistema') if report.type == 'system' else _('Personalizado') }}
62
+ </span>
63
+ </div>
64
+ {% if report.category %}
65
+ <p class="text-muted small mb-2">
66
+ <i class="bi bi-folder me-1"></i>{{ report.category|capitalize }}
67
+ </p>
68
+ {% endif %}
69
+ <p class="card-text">{{ report.description or _('Sin descripción') }}</p>
70
+ </div>
71
+ <div class="card-footer bg-transparent">
72
+ <div class="d-flex gap-2">
73
+ <a href="{{ url_for('report.execute_form', report_id=report.id) }}" class="btn btn-sm btn-primary">
74
+ <i class="bi bi-play-fill me-1"></i>{{ _('Ejecutar') }}
75
+ </a>
76
+ <a href="{{ url_for('report.detail', report_id=report.id) }}" class="btn btn-sm btn-outline-secondary">
77
+ <i class="bi bi-info-circle me-1"></i>{{ _('Detalles') }}
78
+ </a>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ {% else %}
84
+ <div class="col-12">
85
+ <div class="alert alert-info">
86
+ <i class="bi bi-info-circle me-2"></i>{{ _('No hay reportes disponibles.') }}
87
+ </div>
88
+ </div>
89
+ {% endfor %}
90
+ </div>
91
+
92
+ <!-- Pagination -->
93
+ {{ render_pagination(pagination, 'report.index') }}
94
+ </div>
95
+ {% endblock %}