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,156 @@
1
+ {% extends 'base.html' %}
2
+ {% from 'macros.html' import render_messages %}
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>
10
+ <i class="bi bi-check-circle me-2"></i>
11
+ {{ _('Aprobar/Rechazar Préstamo') }}
12
+ </h2>
13
+ <a href="{{ url_for('prestamo.detail', prestamo_id=prestamo.id) }}" class="btn btn-outline-secondary">
14
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
15
+ </a>
16
+ </div>
17
+
18
+ <!-- Loan Summary -->
19
+ <div class="card mb-4">
20
+ <div class="card-header bg-info text-white">
21
+ <h5 class="mb-0">{{ _('Información del Préstamo') }}</h5>
22
+ </div>
23
+ <div class="card-body">
24
+ <div class="row">
25
+ <div class="col-md-6">
26
+ <p><strong>{{ _('Empleado:') }}</strong> {{ prestamo.empleado.primer_nombre }} {{ prestamo.empleado.primer_apellido }}</p>
27
+ <p><strong>{{ _('Código:') }}</strong> {{ prestamo.empleado.codigo_empleado }}</p>
28
+ <p><strong>{{ _('Tipo:') }}</strong>
29
+ {% if prestamo.tipo == 'adelanto' %}
30
+ {{ _('Adelanto de Salario') }}
31
+ {% else %}
32
+ {{ _('Préstamo') }}
33
+ {% endif %}
34
+ </p>
35
+ </div>
36
+ <div class="col-md-6">
37
+ <p><strong>{{ _('Monto Solicitado:') }}</strong>
38
+ {% if prestamo.moneda %}
39
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
40
+ {% endif %}
41
+ {{ "{:,.2f}".format(prestamo.monto_solicitado) }}
42
+ </p>
43
+ <p><strong>{{ _('Cuotas:') }}</strong> {{ prestamo.cuotas_pactadas }}</p>
44
+ <p><strong>{{ _('Fecha de Solicitud:') }}</strong> {{ prestamo.fecha_solicitud.strftime('%d/%m/%Y') }}</p>
45
+ </div>
46
+ </div>
47
+ {% if prestamo.motivo %}
48
+ <div class="row">
49
+ <div class="col-12">
50
+ <p><strong>{{ _('Motivo:') }}</strong> {{ prestamo.motivo }}</p>
51
+ </div>
52
+ </div>
53
+ {% endif %}
54
+ </div>
55
+ </div>
56
+
57
+ <!-- Approval Form -->
58
+ <div class="card">
59
+ <div class="card-body">
60
+ <form method="POST" novalidate>
61
+ {{ form.hidden_tag() }}
62
+
63
+ <div class="row mb-3">
64
+ <div class="col-md-4">
65
+ <label for="{{ form.monto_aprobado.id }}" class="form-label">
66
+ {{ form.monto_aprobado.label.text }} <span class="text-danger">*</span>
67
+ </label>
68
+ {{ form.monto_aprobado(class="form-control" + (" is-invalid" if form.monto_aprobado.errors else "")) }}
69
+ {% if form.monto_aprobado.errors %}
70
+ <div class="invalid-feedback">{{ form.monto_aprobado.errors[0] }}</div>
71
+ {% endif %}
72
+ {% if form.monto_aprobado.description %}
73
+ <div class="form-text">{{ form.monto_aprobado.description }}</div>
74
+ {% endif %}
75
+ </div>
76
+
77
+ <div class="col-md-4">
78
+ <label for="{{ form.fecha_aprobacion.id }}" class="form-label">
79
+ {{ form.fecha_aprobacion.label.text }} <span class="text-danger">*</span>
80
+ </label>
81
+ {{ form.fecha_aprobacion(class="form-control" + (" is-invalid" if form.fecha_aprobacion.errors else "")) }}
82
+ {% if form.fecha_aprobacion.errors %}
83
+ <div class="invalid-feedback">{{ form.fecha_aprobacion.errors[0] }}</div>
84
+ {% endif %}
85
+ </div>
86
+
87
+ <div class="col-md-4">
88
+ <label for="{{ form.fecha_desembolso.id }}" class="form-label">
89
+ {{ form.fecha_desembolso.label.text }}
90
+ </label>
91
+ {{ form.fecha_desembolso(class="form-control" + (" is-invalid" if form.fecha_desembolso.errors else "")) }}
92
+ {% if form.fecha_desembolso.errors %}
93
+ <div class="invalid-feedback">{{ form.fecha_desembolso.errors[0] }}</div>
94
+ {% endif %}
95
+ {% if form.fecha_desembolso.description %}
96
+ <div class="form-text">{{ form.fecha_desembolso.description }}</div>
97
+ {% endif %}
98
+ </div>
99
+ </div>
100
+
101
+ <div class="row mb-3">
102
+ <div class="col-md-6">
103
+ <label for="{{ form.monto_por_cuota.id }}" class="form-label">
104
+ {{ form.monto_por_cuota.label.text }}
105
+ </label>
106
+ {{ form.monto_por_cuota(class="form-control" + (" is-invalid" if form.monto_por_cuota.errors else ""), readonly=true) }}
107
+ {% if form.monto_por_cuota.errors %}
108
+ <div class="invalid-feedback">{{ form.monto_por_cuota.errors[0] }}</div>
109
+ {% endif %}
110
+ {% if form.monto_por_cuota.description %}
111
+ <div class="form-text">{{ form.monto_por_cuota.description }}</div>
112
+ {% endif %}
113
+ </div>
114
+ </div>
115
+
116
+ <!-- Rejection Section -->
117
+ <div class="row mb-4">
118
+ <div class="col-12">
119
+ <label for="{{ form.motivo_rechazo.id }}" class="form-label">
120
+ {{ form.motivo_rechazo.label.text }}
121
+ </label>
122
+ {{ form.motivo_rechazo(class="form-control" + (" is-invalid" if form.motivo_rechazo.errors else ""), rows=3) }}
123
+ {% if form.motivo_rechazo.errors %}
124
+ <div class="invalid-feedback">{{ form.motivo_rechazo.errors[0] }}</div>
125
+ {% endif %}
126
+ {% if form.motivo_rechazo.description %}
127
+ <div class="form-text">{{ form.motivo_rechazo.description }}</div>
128
+ {% endif %}
129
+ </div>
130
+ </div>
131
+
132
+ <div class="d-flex justify-content-end gap-2">
133
+ <a href="{{ url_for('prestamo.detail', prestamo_id=prestamo.id) }}" class="btn btn-secondary">
134
+ {{ _('Cancelar') }}
135
+ </a>
136
+ {{ form.rechazar(class="btn btn-danger", onclick="return confirm('" + _('¿Está seguro de rechazar este préstamo?') + "');") }}
137
+ {{ form.aprobar(class="btn btn-success") }}
138
+ </div>
139
+ </form>
140
+ </div>
141
+ </div>
142
+
143
+ <script>
144
+ // Auto-calculate installment amount
145
+ document.getElementById('{{ form.monto_aprobado.id }}').addEventListener('input', function() {
146
+ const monto = parseFloat(this.value) || 0;
147
+ const cuotas = {{ prestamo.cuotas_pactadas }};
148
+ const montoCuota = cuotas > 0 ? (monto / cuotas).toFixed(2) : 0;
149
+ document.getElementById('{{ form.monto_por_cuota.id }}').value = montoCuota;
150
+ });
151
+
152
+ // Trigger calculation on page load
153
+ document.getElementById('{{ form.monto_aprobado.id }}').dispatchEvent(new Event('input'));
154
+ </script>
155
+ </div>
156
+ {% endblock %}
@@ -0,0 +1,249 @@
1
+ {% extends 'base.html' %}
2
+ {% from 'macros.html' import render_messages %}
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>
10
+ <i class="bi bi-heart me-2"></i>
11
+ {{ _('Condonación de Deuda') }}
12
+ </h2>
13
+ <a href="{{ url_for('prestamo.detail', prestamo_id=prestamo.id) }}" class="btn btn-outline-secondary">
14
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
15
+ </a>
16
+ </div>
17
+
18
+ <!-- Warning Alert -->
19
+ <div class="alert alert-warning">
20
+ <i class="bi bi-exclamation-triangle me-2"></i>
21
+ <strong>{{ _('Advertencia:') }}</strong> {{ _('La condonación de deuda es una operación irreversible que reduce el saldo pendiente del préstamo sin que el empleado realice un pago. Esta acción requiere autorización gerencial y documentación completa para auditoría.') }}
22
+ </div>
23
+
24
+ <!-- Loan Summary -->
25
+ <div class="card mb-4">
26
+ <div class="card-header bg-info text-white">
27
+ <h5 class="mb-0">{{ _('Información del Préstamo') }}</h5>
28
+ </div>
29
+ <div class="card-body">
30
+ <div class="row">
31
+ <div class="col-md-4">
32
+ <p><strong>{{ _('Empleado:') }}</strong> {{ prestamo.empleado.primer_nombre }} {{ prestamo.empleado.primer_apellido }}</p>
33
+ <p><strong>{{ _('Código:') }}</strong> {{ prestamo.empleado.codigo_empleado }}</p>
34
+ <p><strong>{{ _('Tipo:') }}</strong>
35
+ {% if prestamo.tipo == 'adelanto' %}
36
+ {{ _('Adelanto de Salario') }}
37
+ {% else %}
38
+ {{ _('Préstamo') }}
39
+ {% endif %}
40
+ </p>
41
+ </div>
42
+ <div class="col-md-4">
43
+ <p><strong>{{ _('Monto Original:') }}</strong>
44
+ {% if prestamo.moneda %}
45
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
46
+ {% endif %}
47
+ {{ "{:,.2f}".format(prestamo.monto_aprobado or prestamo.monto_solicitado) }}
48
+ </p>
49
+ <p><strong>{{ _('Saldo Pendiente Actual:') }}</strong>
50
+ <span class="text-danger fs-5">
51
+ {% if prestamo.moneda %}
52
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
53
+ {% endif %}
54
+ {{ "{:,.2f}".format(prestamo.saldo_pendiente) }}
55
+ </span>
56
+ </p>
57
+ </div>
58
+ <div class="col-md-4">
59
+ <p><strong>{{ _('Cuotas Pactadas:') }}</strong> {{ prestamo.cuotas_pactadas }}</p>
60
+ <p><strong>{{ _('Monto por Cuota:') }}</strong>
61
+ {% if prestamo.moneda %}
62
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
63
+ {% endif %}
64
+ {{ "{:,.2f}".format(prestamo.monto_por_cuota or 0) }}
65
+ </p>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <!-- Forgiveness Form -->
72
+ <div class="card">
73
+ <div class="card-header bg-danger text-white">
74
+ <h5 class="mb-0">{{ _('Registro de Condonación') }}</h5>
75
+ </div>
76
+ <div class="card-body">
77
+ <form method="POST" novalidate>
78
+ {{ form.hidden_tag() }}
79
+
80
+ <h6 class="mb-3">{{ _('Datos de la Condonación') }}</h6>
81
+
82
+ <div class="row mb-3">
83
+ <div class="col-md-4">
84
+ <label for="{{ form.fecha_condonacion.id }}" class="form-label">
85
+ {{ form.fecha_condonacion.label.text }} <span class="text-danger">*</span>
86
+ </label>
87
+ {{ form.fecha_condonacion(class="form-control" + (" is-invalid" if form.fecha_condonacion.errors else "")) }}
88
+ {% if form.fecha_condonacion.errors %}
89
+ <div class="invalid-feedback">{{ form.fecha_condonacion.errors[0] }}</div>
90
+ {% endif %}
91
+ </div>
92
+
93
+ <div class="col-md-4">
94
+ <label for="{{ form.monto_condonado.id }}" class="form-label">
95
+ {{ form.monto_condonado.label.text }} <span class="text-danger">*</span>
96
+ </label>
97
+ <div class="input-group">
98
+ {% if prestamo.moneda %}
99
+ <span class="input-group-text">{{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}</span>
100
+ {% endif %}
101
+ {{ form.monto_condonado(class="form-control" + (" is-invalid" if form.monto_condonado.errors else ""), id="monto_condonado") }}
102
+ {% if form.monto_condonado.errors %}
103
+ <div class="invalid-feedback">{{ form.monto_condonado.errors[0] }}</div>
104
+ {% endif %}
105
+ </div>
106
+ {% if form.monto_condonado.description %}
107
+ <div class="form-text">{{ form.monto_condonado.description }}</div>
108
+ {% endif %}
109
+ </div>
110
+
111
+ <div class="col-md-4">
112
+ <label for="{{ form.porcentaje_condonado.id }}" class="form-label">
113
+ {{ form.porcentaje_condonado.label.text }}
114
+ </label>
115
+ <div class="input-group">
116
+ {{ form.porcentaje_condonado(class="form-control" + (" is-invalid" if form.porcentaje_condonado.errors else ""), id="porcentaje_condonado", readonly=true) }}
117
+ <span class="input-group-text">%</span>
118
+ {% if form.porcentaje_condonado.errors %}
119
+ <div class="invalid-feedback">{{ form.porcentaje_condonado.errors[0] }}</div>
120
+ {% endif %}
121
+ </div>
122
+ {% if form.porcentaje_condonado.description %}
123
+ <div class="form-text">{{ form.porcentaje_condonado.description }}</div>
124
+ {% endif %}
125
+ </div>
126
+ </div>
127
+
128
+ <hr class="my-4">
129
+ <h6 class="mb-3">{{ _('Autorización y Documentación') }}</h6>
130
+
131
+ <div class="row mb-3">
132
+ <div class="col-md-6">
133
+ <label for="{{ form.autorizado_por.id }}" class="form-label">
134
+ {{ form.autorizado_por.label.text }} <span class="text-danger">*</span>
135
+ </label>
136
+ {{ form.autorizado_por(class="form-control" + (" is-invalid" if form.autorizado_por.errors else ""), placeholder="Ej: Lic. Juan Pérez - Gerente de Finanzas") }}
137
+ {% if form.autorizado_por.errors %}
138
+ <div class="invalid-feedback">{{ form.autorizado_por.errors[0] }}</div>
139
+ {% endif %}
140
+ {% if form.autorizado_por.description %}
141
+ <div class="form-text">{{ form.autorizado_por.description }}</div>
142
+ {% endif %}
143
+ </div>
144
+
145
+ <div class="col-md-6">
146
+ <label for="{{ form.documento_soporte.id }}" class="form-label">
147
+ {{ form.documento_soporte.label.text }} <span class="text-danger">*</span>
148
+ </label>
149
+ {{ form.documento_soporte(class="form-select" + (" is-invalid" if form.documento_soporte.errors else "")) }}
150
+ {% if form.documento_soporte.errors %}
151
+ <div class="invalid-feedback">{{ form.documento_soporte.errors[0] }}</div>
152
+ {% endif %}
153
+ {% if form.documento_soporte.description %}
154
+ <div class="form-text">{{ form.documento_soporte.description }}</div>
155
+ {% endif %}
156
+ </div>
157
+ </div>
158
+
159
+ <div class="row mb-3">
160
+ <div class="col-12">
161
+ <label for="{{ form.referencia_documento.id }}" class="form-label">
162
+ {{ form.referencia_documento.label.text }} <span class="text-danger">*</span>
163
+ </label>
164
+ {{ form.referencia_documento(class="form-control" + (" is-invalid" if form.referencia_documento.errors else ""), placeholder="Ej: Correo del 15/12/2024, Memorándum No. 045-2024, Acta de Junta No. 12") }}
165
+ {% if form.referencia_documento.errors %}
166
+ <div class="invalid-feedback">{{ form.referencia_documento.errors[0] }}</div>
167
+ {% endif %}
168
+ {% if form.referencia_documento.description %}
169
+ <div class="form-text">{{ form.referencia_documento.description }}</div>
170
+ {% endif %}
171
+ </div>
172
+ </div>
173
+
174
+ <div class="row mb-3">
175
+ <div class="col-12">
176
+ <label for="{{ form.justificacion.id }}" class="form-label">
177
+ {{ form.justificacion.label.text }} <span class="text-danger">*</span>
178
+ </label>
179
+ {% set placeholder_text = "Ej: Según correo electrónico recibido en fecha 15/12/2024, el Gerente de Finanzas ha autorizado descontar el 25% del saldo del préstamo " ~ prestamo.id[:8] ~ " al empleado " ~ prestamo.empleado.primer_nombre ~ " " ~ prestamo.empleado.primer_apellido ~ " debido a [razón específica]. Se adjunta correo electrónico como evidencia de la autorización." %}
180
+ {{ form.justificacion(class="form-control" + (" is-invalid" if form.justificacion.errors else ""), rows=5, placeholder=placeholder_text) }}
181
+ {% if form.justificacion.errors %}
182
+ <div class="invalid-feedback">{{ form.justificacion.errors[0] }}</div>
183
+ {% endif %}
184
+ {% if form.justificacion.description %}
185
+ <div class="form-text">{{ form.justificacion.description }}</div>
186
+ {% endif %}
187
+ </div>
188
+ </div>
189
+
190
+ <hr class="my-4">
191
+ <h6 class="mb-3">{{ _('Información Contable (Opcional)') }}</h6>
192
+
193
+ <div class="row mb-3">
194
+ <div class="col-md-6">
195
+ <label for="{{ form.cuenta_debe.id }}" class="form-label">
196
+ {{ form.cuenta_debe.label.text }}
197
+ </label>
198
+ {{ form.cuenta_debe(class="form-control" + (" is-invalid" if form.cuenta_debe.errors else "")) }}
199
+ {% if form.cuenta_debe.errors %}
200
+ <div class="invalid-feedback">{{ form.cuenta_debe.errors[0] }}</div>
201
+ {% endif %}
202
+ {% if form.cuenta_debe.description %}
203
+ <div class="form-text">{{ form.cuenta_debe.description }}</div>
204
+ {% endif %}
205
+ </div>
206
+
207
+ <div class="col-md-6">
208
+ <label for="{{ form.cuenta_haber.id }}" class="form-label">
209
+ {{ form.cuenta_haber.label.text }}
210
+ </label>
211
+ {{ form.cuenta_haber(class="form-control" + (" is-invalid" if form.cuenta_haber.errors else "")) }}
212
+ {% if form.cuenta_haber.errors %}
213
+ <div class="invalid-feedback">{{ form.cuenta_haber.errors[0] }}</div>
214
+ {% endif %}
215
+ {% if form.cuenta_haber.description %}
216
+ <div class="form-text">{{ form.cuenta_haber.description }}</div>
217
+ {% endif %}
218
+ </div>
219
+ </div>
220
+
221
+ <div class="alert alert-danger mt-4">
222
+ <i class="bi bi-shield-exclamation me-2"></i>
223
+ <strong>{{ _('Importante:') }}</strong> {{ _('Esta información quedará registrada permanentemente en el sistema para fines de auditoría. Asegúrese de que toda la documentación esté completa y correcta.') }}
224
+ </div>
225
+
226
+ <div class="d-flex justify-content-end gap-2 mt-4">
227
+ <a href="{{ url_for('prestamo.detail', prestamo_id=prestamo.id) }}" class="btn btn-secondary">
228
+ {{ _('Cancelar') }}
229
+ </a>
230
+ {{ form.submit(class="btn btn-danger", onclick="return confirm('" + _('¿Está seguro de condonar esta deuda? Esta acción no se puede deshacer.') + "');") }}
231
+ </div>
232
+ </form>
233
+ </div>
234
+ </div>
235
+ </div>
236
+
237
+ <script>
238
+ // Auto-calculate percentage when amount changes
239
+ document.getElementById('monto_condonado').addEventListener('input', function() {
240
+ const monto = parseFloat(this.value) || 0;
241
+ const saldo = {{ prestamo.saldo_pendiente }};
242
+ const porcentaje = saldo > 0 ? ((monto / saldo) * 100).toFixed(2) : 0;
243
+ document.getElementById('porcentaje_condonado').value = porcentaje;
244
+ });
245
+
246
+ // Trigger calculation on page load
247
+ document.getElementById('monto_condonado').dispatchEvent(new Event('input'));
248
+ </script>
249
+ {% endblock %}