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,443 @@
1
+ {% extends 'base.html' %}
2
+ {% from 'macros.html' import render_messages %}
3
+
4
+ {% block content %}
5
+ <div class="container-fluid">
6
+ {{ render_messages() }}
7
+
8
+ <div class="d-flex justify-content-between align-items-center mb-4">
9
+ <h2>
10
+ <i class="bi bi-cash-coin me-2"></i>
11
+ {{ _('Detalle de Préstamo/Adelanto') }}
12
+ </h2>
13
+ <a href="{{ url_for('prestamo.index') }}" class="btn btn-outline-secondary">
14
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
15
+ </a>
16
+ </div>
17
+
18
+ <div class="row">
19
+ <!-- Loan Information Card -->
20
+ <div class="col-lg-6 mb-4">
21
+ <div class="card">
22
+ <div class="card-header bg-primary text-white">
23
+ <h5 class="mb-0">{{ _('Información General') }}</h5>
24
+ </div>
25
+ <div class="card-body">
26
+ <div class="row mb-2">
27
+ <div class="col-6 fw-bold">{{ _('Estado:') }}</div>
28
+ <div class="col-6">
29
+ {% if prestamo.estado == 'borrador' %}
30
+ <span class="badge bg-secondary">{{ _('Borrador') }}</span>
31
+ {% elif prestamo.estado == 'pendiente' %}
32
+ <span class="badge bg-warning text-dark">{{ _('Pendiente') }}</span>
33
+ {% elif prestamo.estado == 'aprobado' %}
34
+ <span class="badge bg-success">{{ _('Aprobado') }}</span>
35
+ {% elif prestamo.estado == 'aplicado' %}
36
+ <span class="badge bg-success">{{ _('Aplicado') }}</span>
37
+ {% elif prestamo.estado == 'pagado' %}
38
+ <span class="badge bg-dark">{{ _('Pagado') }}</span>
39
+ {% elif prestamo.estado == 'rechazado' %}
40
+ <span class="badge bg-danger">{{ _('Rechazado') }}</span>
41
+ {% elif prestamo.estado == 'cancelado' %}
42
+ <span class="badge bg-secondary">{{ _('Cancelado') }}</span>
43
+ {% endif %}
44
+ </div>
45
+ </div>
46
+ <div class="row mb-2">
47
+ <div class="col-6 fw-bold">{{ _('Tipo:') }}</div>
48
+ <div class="col-6">
49
+ {% if prestamo.tipo == 'adelanto' %}
50
+ <span class="badge bg-info">{{ _('Adelanto de Salario') }}</span>
51
+ {% else %}
52
+ <span class="badge bg-primary">{{ _('Préstamo') }}</span>
53
+ {% endif %}
54
+ </div>
55
+ </div>
56
+ <div class="row mb-2">
57
+ <div class="col-6 fw-bold">{{ _('Empleado:') }}</div>
58
+ <div class="col-6">
59
+ {{ prestamo.empleado.primer_nombre }} {{ prestamo.empleado.primer_apellido }}<br>
60
+ <small class="text-muted">{{ prestamo.empleado.codigo_empleado }}</small>
61
+ </div>
62
+ </div>
63
+ <div class="row mb-2">
64
+ <div class="col-6 fw-bold">{{ _('Fecha de Solicitud:') }}</div>
65
+ <div class="col-6">{{ prestamo.fecha_solicitud.strftime('%d/%m/%Y') }}</div>
66
+ </div>
67
+ {% if prestamo.fecha_aprobacion %}
68
+ <div class="row mb-2">
69
+ <div class="col-6 fw-bold">{{ _('Fecha de Aprobación:') }}</div>
70
+ <div class="col-6">{{ prestamo.fecha_aprobacion.strftime('%d/%m/%Y') }}</div>
71
+ </div>
72
+ {% endif %}
73
+ {% if prestamo.fecha_desembolso %}
74
+ <div class="row mb-2">
75
+ <div class="col-6 fw-bold">{{ _('Fecha de Desembolso:') }}</div>
76
+ <div class="col-6">{{ prestamo.fecha_desembolso.strftime('%d/%m/%Y') }}</div>
77
+ </div>
78
+ {% endif %}
79
+ {% if prestamo.motivo %}
80
+ <div class="row mb-2">
81
+ <div class="col-6 fw-bold">{{ _('Motivo:') }}</div>
82
+ <div class="col-6">{{ prestamo.motivo }}</div>
83
+ </div>
84
+ {% endif %}
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ <!-- Financial Details Card -->
90
+ <div class="col-lg-6 mb-4">
91
+ <div class="card">
92
+ <div class="card-header bg-success text-white">
93
+ <h5 class="mb-0">{{ _('Información Financiera') }}</h5>
94
+ </div>
95
+ <div class="card-body">
96
+ <div class="row mb-2">
97
+ <div class="col-6 fw-bold">{{ _('Monto Solicitado:') }}</div>
98
+ <div class="col-6">
99
+ {% if prestamo.moneda %}
100
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
101
+ {% endif %}
102
+ {{ "{:,.2f}".format(prestamo.monto_solicitado) }}
103
+ </div>
104
+ </div>
105
+ {% if prestamo.monto_aprobado %}
106
+ <div class="row mb-2">
107
+ <div class="col-6 fw-bold">{{ _('Monto Aprobado:') }}</div>
108
+ <div class="col-6 text-success">
109
+ {% if prestamo.moneda %}
110
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
111
+ {% endif %}
112
+ {{ "{:,.2f}".format(prestamo.monto_aprobado) }}
113
+ </div>
114
+ </div>
115
+ {% endif %}
116
+ <div class="row mb-2">
117
+ <div class="col-6 fw-bold">{{ _('Saldo Pendiente:') }}</div>
118
+ <div class="col-6 text-danger fs-5">
119
+ {% if prestamo.moneda %}
120
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
121
+ {% endif %}
122
+ {{ "{:,.2f}".format(prestamo.saldo_pendiente) }}
123
+ </div>
124
+ </div>
125
+ <div class="row mb-2">
126
+ <div class="col-6 fw-bold">{{ _('Número de Cuotas:') }}</div>
127
+ <div class="col-6">{{ prestamo.cuotas_pactadas }}</div>
128
+ </div>
129
+ <div class="row mb-2">
130
+ <div class="col-6 fw-bold">{{ _('Monto por Cuota:') }}</div>
131
+ <div class="col-6">
132
+ {% if prestamo.moneda %}
133
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
134
+ {% endif %}
135
+ {{ "{:,.2f}".format(prestamo.monto_por_cuota or 0) }}
136
+ </div>
137
+ </div>
138
+ {% if prestamo.tasa_interes and prestamo.tasa_interes > 0 %}
139
+ <div class="row mb-2">
140
+ <div class="col-6 fw-bold">{{ _('Tasa de Interés:') }}</div>
141
+ <div class="col-6">{{ "{:.4f}".format(prestamo.tasa_interes) }}%</div>
142
+ </div>
143
+ <div class="row mb-2">
144
+ <div class="col-6 fw-bold">{{ _('Tipo de Interés:') }}</div>
145
+ <div class="col-6">{{ prestamo.tipo_interes|title }}</div>
146
+ </div>
147
+ <div class="row mb-2">
148
+ <div class="col-6 fw-bold">{{ _('Método de Amortización:') }}</div>
149
+ <div class="col-6">
150
+ {% if prestamo.metodo_amortizacion == 'frances' %}
151
+ {{ _('Francés - Cuota Constante') }}
152
+ {% elif prestamo.metodo_amortizacion == 'aleman' %}
153
+ {{ _('Alemán - Amortización Constante') }}
154
+ {% else %}
155
+ {{ prestamo.metodo_amortizacion|title }}
156
+ {% endif %}
157
+ </div>
158
+ </div>
159
+ <div class="row mb-2">
160
+ <div class="col-6 fw-bold">{{ _('Interés Acumulado:') }}</div>
161
+ <div class="col-6 text-warning">
162
+ {% if prestamo.moneda %}
163
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
164
+ {% endif %}
165
+ {{ "{:,.2f}".format(prestamo.interes_acumulado or 0) }}
166
+ </div>
167
+ </div>
168
+ {% endif %}
169
+ </div>
170
+ </div>
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Action Buttons -->
175
+ <div class="row mb-4">
176
+ <div class="col-12">
177
+ <div class="card">
178
+ <div class="card-body">
179
+ <div class="d-flex gap-2 flex-wrap">
180
+ {% if prestamo.estado == 'borrador' %}
181
+ <a href="{{ url_for('prestamo.edit', prestamo_id=prestamo.id) }}" class="btn btn-warning">
182
+ <i class="bi bi-pencil me-1"></i>{{ _('Editar') }}
183
+ </a>
184
+ <form method="POST" action="{{ url_for('prestamo.submit', prestamo_id=prestamo.id) }}" class="d-inline">
185
+ <button type="submit" class="btn btn-primary">
186
+ <i class="bi bi-send me-1"></i>{{ _('Enviar para Aprobación') }}
187
+ </button>
188
+ </form>
189
+ {% endif %}
190
+
191
+ {% if prestamo.estado in ['pendiente', 'borrador'] %}
192
+ <a href="{{ url_for('prestamo.approve', prestamo_id=prestamo.id) }}" class="btn btn-success">
193
+ <i class="bi bi-check-circle me-1"></i>{{ _('Aprobar/Rechazar') }}
194
+ </a>
195
+ {% endif %}
196
+
197
+ {% if prestamo.estado in ['aprobado', 'aplicado'] and prestamo.saldo_pendiente > 0 %}
198
+ <a href="{{ url_for('prestamo.pago_extraordinario', prestamo_id=prestamo.id) }}" class="btn btn-info">
199
+ <i class="bi bi-cash-stack me-1"></i>{{ _('Registrar Pago Extraordinario') }}
200
+ </a>
201
+ <a href="{{ url_for('prestamo.condonacion', prestamo_id=prestamo.id) }}" class="btn btn-warning">
202
+ <i class="bi bi-heart me-1"></i>{{ _('Condonar Deuda') }}
203
+ </a>
204
+ {% endif %}
205
+
206
+ {% if prestamo.estado not in ['pagado', 'cancelado'] %}
207
+ <form method="POST" action="{{ url_for('prestamo.cancel', prestamo_id=prestamo.id) }}" class="d-inline"
208
+ onsubmit="return confirm('{{ _('¿Está seguro de cancelar este préstamo?') }}');">
209
+ <button type="submit" class="btn btn-danger">
210
+ <i class="bi bi-x-circle me-1"></i>{{ _('Cancelar Préstamo') }}
211
+ </button>
212
+ </form>
213
+ {% endif %}
214
+
215
+ {% if tabla_pago %}
216
+ <a href="{{ url_for('prestamo.export_excel', prestamo_id=prestamo.id) }}" class="btn btn-success">
217
+ <i class="bi bi-file-earmark-excel me-1"></i>{{ _('Exportar Excel') }}
218
+ </a>
219
+ <a href="{{ url_for('prestamo.export_pdf', prestamo_id=prestamo.id) }}" class="btn btn-danger">
220
+ <i class="bi bi-file-earmark-pdf me-1"></i>{{ _('Exportar PDF') }}
221
+ </a>
222
+ {% endif %}
223
+ </div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+ </div>
228
+
229
+ <!-- Payment Schedule -->
230
+ {% if tabla_pago %}
231
+ <div class="row mb-4">
232
+ <div class="col-12">
233
+ <div class="card">
234
+ <div class="card-header bg-info text-white">
235
+ <h5 class="mb-0">{{ _('Tabla de Pagos') }}</h5>
236
+ </div>
237
+ <div class="card-body">
238
+ <div class="table-responsive">
239
+ <table class="table table-striped table-hover">
240
+ <thead>
241
+ <tr>
242
+ <th>#</th>
243
+ <th>{{ _('Fecha Estimada') }}</th>
244
+ <th class="text-end">{{ _('Cuota') }}</th>
245
+ <th class="text-end">{{ _('Interés') }}</th>
246
+ <th class="text-end">{{ _('Capital') }}</th>
247
+ <th class="text-end">{{ _('Saldo') }}</th>
248
+ </tr>
249
+ </thead>
250
+ <tbody>
251
+ {% for item in tabla_pago %}
252
+ <tr>
253
+ <td>{{ item.numero }}</td>
254
+ <td>
255
+ {% if item.fecha_estimada %}
256
+ {{ item.fecha_estimada.strftime('%d/%m/%Y') }}
257
+ {% else %}
258
+ -
259
+ {% endif %}
260
+ </td>
261
+ <td class="text-end">
262
+ {% if prestamo.moneda %}
263
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
264
+ {% endif %}
265
+ {{ "{:,.2f}".format(item.cuota) }}
266
+ </td>
267
+ <td class="text-end">
268
+ {% if prestamo.moneda %}
269
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
270
+ {% endif %}
271
+ {{ "{:,.2f}".format(item.interes) }}
272
+ </td>
273
+ <td class="text-end">
274
+ {% if prestamo.moneda %}
275
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
276
+ {% endif %}
277
+ {{ "{:,.2f}".format(item.capital) }}
278
+ </td>
279
+ <td class="text-end">
280
+ {% if prestamo.moneda %}
281
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
282
+ {% endif %}
283
+ {{ "{:,.2f}".format(item.saldo) }}
284
+ </td>
285
+ </tr>
286
+ {% endfor %}
287
+ </tbody>
288
+ </table>
289
+ </div>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ {% endif %}
295
+
296
+ <!-- Interest Journal -->
297
+ {% if prestamo.tasa_interes and prestamo.tasa_interes > 0 %}
298
+ <div class="row mb-4">
299
+ <div class="col-12">
300
+ <div class="card">
301
+ <div class="card-header bg-warning text-dark">
302
+ <h5 class="mb-0">{{ _('Diario de Intereses') }}</h5>
303
+ </div>
304
+ <div class="card-body">
305
+ {% if intereses %}
306
+ <div class="table-responsive">
307
+ <table class="table table-striped table-sm">
308
+ <thead>
309
+ <tr>
310
+ <th>{{ _('Período') }}</th>
311
+ <th>{{ _('Días') }}</th>
312
+ <th class="text-end">{{ _('Saldo Base') }}</th>
313
+ <th class="text-end">{{ _('Tasa (%)') }}</th>
314
+ <th class="text-end">{{ _('Interés Calculado') }}</th>
315
+ <th class="text-end">{{ _('Saldo Posterior') }}</th>
316
+ <th>{{ _('Nómina') }}</th>
317
+ </tr>
318
+ </thead>
319
+ <tbody>
320
+ {% for interes in intereses %}
321
+ <tr>
322
+ <td>
323
+ {{ interes.fecha_desde.strftime('%d/%m/%Y') }} -
324
+ {{ interes.fecha_hasta.strftime('%d/%m/%Y') }}
325
+ </td>
326
+ <td>{{ interes.dias_transcurridos }}</td>
327
+ <td class="text-end">
328
+ {% if prestamo.moneda %}
329
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
330
+ {% endif %}
331
+ {{ "{:,.2f}".format(interes.saldo_base) }}
332
+ </td>
333
+ <td class="text-end">{{ "{:.4f}".format(interes.tasa_aplicada) }}</td>
334
+ <td class="text-end text-warning fw-bold">
335
+ {% if prestamo.moneda %}
336
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
337
+ {% endif %}
338
+ {{ "{:,.2f}".format(interes.interes_calculado) }}
339
+ </td>
340
+ <td class="text-end">
341
+ {% if prestamo.moneda %}
342
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
343
+ {% endif %}
344
+ {{ "{:,.2f}".format(interes.saldo_posterior) }}
345
+ </td>
346
+ <td>
347
+ {% if interes.nomina_id %}
348
+ <span class="badge bg-primary">{{ _('Nómina') }}</span>
349
+ {% else %}
350
+ -
351
+ {% endif %}
352
+ </td>
353
+ </tr>
354
+ {% endfor %}
355
+ </tbody>
356
+ </table>
357
+ </div>
358
+ {% else %}
359
+ <p class="text-muted text-center py-4">{{ _('No se han calculado intereses aún.') }}</p>
360
+ {% endif %}
361
+ </div>
362
+ </div>
363
+ </div>
364
+ </div>
365
+ {% endif %}
366
+
367
+ <!-- Payment History -->
368
+ <div class="row">
369
+ <div class="col-12">
370
+ <div class="card">
371
+ <div class="card-header bg-secondary text-white">
372
+ <h5 class="mb-0">{{ _('Historial de Pagos') }}</h5>
373
+ </div>
374
+ <div class="card-body">
375
+ {% if abonos %}
376
+ <div class="table-responsive">
377
+ <table class="table table-striped">
378
+ <thead>
379
+ <tr>
380
+ <th>{{ _('Fecha') }}</th>
381
+ <th>{{ _('Tipo') }}</th>
382
+ <th class="text-end">{{ _('Monto Abonado') }}</th>
383
+ <th class="text-end">{{ _('Saldo Anterior') }}</th>
384
+ <th class="text-end">{{ _('Saldo Posterior') }}</th>
385
+ <th>{{ _('Comprobante') }}</th>
386
+ <th>{{ _('Observaciones') }}</th>
387
+ </tr>
388
+ </thead>
389
+ <tbody>
390
+ {% for abono in abonos %}
391
+ <tr>
392
+ <td>{{ abono.fecha_abono.strftime('%d/%m/%Y') }}</td>
393
+ <td>
394
+ {% if abono.tipo_abono == 'manual' %}
395
+ <span class="badge bg-warning text-dark">{{ _('Manual') }}</span>
396
+ {% else %}
397
+ <span class="badge bg-primary">{{ _('Nómina') }}</span>
398
+ {% endif %}
399
+ </td>
400
+ <td class="text-end">
401
+ {% if prestamo.moneda %}
402
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
403
+ {% endif %}
404
+ {{ "{:,.2f}".format(abono.monto_abonado) }}
405
+ </td>
406
+ <td class="text-end">
407
+ {% if prestamo.moneda %}
408
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
409
+ {% endif %}
410
+ {{ "{:,.2f}".format(abono.saldo_anterior) }}
411
+ </td>
412
+ <td class="text-end">
413
+ {% if prestamo.moneda %}
414
+ {{ prestamo.moneda.simbolo or prestamo.moneda.codigo }}
415
+ {% endif %}
416
+ {{ "{:,.2f}".format(abono.saldo_posterior) }}
417
+ </td>
418
+ <td>
419
+ {% if abono.tipo_comprobante %}
420
+ <strong>{{ abono.tipo_comprobante|replace('_', ' ')|title }}:</strong><br>
421
+ {{ abono.numero_comprobante }}
422
+ {% if abono.referencia_bancaria %}
423
+ <br><small class="text-muted">Ref: {{ abono.referencia_bancaria }}</small>
424
+ {% endif %}
425
+ {% else %}
426
+ -
427
+ {% endif %}
428
+ </td>
429
+ <td>{{ abono.observaciones or '-' }}</td>
430
+ </tr>
431
+ {% endfor %}
432
+ </tbody>
433
+ </table>
434
+ </div>
435
+ {% else %}
436
+ <p class="text-muted text-center py-4">{{ _('No hay pagos registrados.') }}</p>
437
+ {% endif %}
438
+ </div>
439
+ </div>
440
+ </div>
441
+ </div>
442
+ </div>
443
+ {% endblock %}
@@ -0,0 +1,203 @@
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-cash-coin me-2"></i>
11
+ {% if prestamo %}
12
+ {{ _('Editar Préstamo/Adelanto') }}
13
+ {% else %}
14
+ {{ _('Nuevo Préstamo/Adelanto') }}
15
+ {% endif %}
16
+ </h2>
17
+ <a href="{{ url_for('prestamo.index') }}" class="btn btn-outline-secondary">
18
+ <i class="bi bi-arrow-left me-1"></i>{{ _('Volver') }}
19
+ </a>
20
+ </div>
21
+
22
+ <div class="card">
23
+ <div class="card-body">
24
+ <form method="POST" novalidate>
25
+ {{ form.hidden_tag() }}
26
+
27
+ <div class="row mb-3">
28
+ <div class="col-md-6">
29
+ <label for="{{ form.empleado_id.id }}" class="form-label">
30
+ {{ form.empleado_id.label.text }} <span class="text-danger">*</span>
31
+ </label>
32
+ {{ form.empleado_id(class="form-select" + (" is-invalid" if form.empleado_id.errors else "")) }}
33
+ {% if form.empleado_id.errors %}
34
+ <div class="invalid-feedback">{{ form.empleado_id.errors[0] }}</div>
35
+ {% endif %}
36
+ {% if form.empleado_id.description %}
37
+ <div class="form-text">{{ form.empleado_id.description }}</div>
38
+ {% endif %}
39
+ </div>
40
+
41
+ <div class="col-md-3">
42
+ <label for="{{ form.tipo.id }}" class="form-label">
43
+ {{ form.tipo.label.text }} <span class="text-danger">*</span>
44
+ </label>
45
+ {{ form.tipo(class="form-select" + (" is-invalid" if form.tipo.errors else "")) }}
46
+ {% if form.tipo.errors %}
47
+ <div class="invalid-feedback">{{ form.tipo.errors[0] }}</div>
48
+ {% endif %}
49
+ {% if form.tipo.description %}
50
+ <div class="form-text">{{ form.tipo.description }}</div>
51
+ {% endif %}
52
+ </div>
53
+
54
+ <div class="col-md-3">
55
+ <label for="{{ form.fecha_solicitud.id }}" class="form-label">
56
+ {{ form.fecha_solicitud.label.text }} <span class="text-danger">*</span>
57
+ </label>
58
+ {{ form.fecha_solicitud(class="form-control" + (" is-invalid" if form.fecha_solicitud.errors else "")) }}
59
+ {% if form.fecha_solicitud.errors %}
60
+ <div class="invalid-feedback">{{ form.fecha_solicitud.errors[0] }}</div>
61
+ {% endif %}
62
+ </div>
63
+ </div>
64
+
65
+ <div class="row mb-3">
66
+ <div class="col-md-4">
67
+ <label for="{{ form.monto_solicitado.id }}" class="form-label">
68
+ {{ form.monto_solicitado.label.text }} <span class="text-danger">*</span>
69
+ </label>
70
+ {{ form.monto_solicitado(class="form-control" + (" is-invalid" if form.monto_solicitado.errors else "")) }}
71
+ {% if form.monto_solicitado.errors %}
72
+ <div class="invalid-feedback">{{ form.monto_solicitado.errors[0] }}</div>
73
+ {% endif %}
74
+ {% if form.monto_solicitado.description %}
75
+ <div class="form-text">{{ form.monto_solicitado.description }}</div>
76
+ {% endif %}
77
+ </div>
78
+
79
+ <div class="col-md-4">
80
+ <label for="{{ form.moneda_id.id }}" class="form-label">
81
+ {{ form.moneda_id.label.text }} <span class="text-danger">*</span>
82
+ </label>
83
+ {{ form.moneda_id(class="form-select" + (" is-invalid" if form.moneda_id.errors else "")) }}
84
+ {% if form.moneda_id.errors %}
85
+ <div class="invalid-feedback">{{ form.moneda_id.errors[0] }}</div>
86
+ {% endif %}
87
+ {% if form.moneda_id.description %}
88
+ <div class="form-text">{{ form.moneda_id.description }}</div>
89
+ {% endif %}
90
+ </div>
91
+
92
+ <div class="col-md-4">
93
+ <label for="{{ form.cuotas_pactadas.id }}" class="form-label">
94
+ {{ form.cuotas_pactadas.label.text }} <span class="text-danger">*</span>
95
+ </label>
96
+ {{ form.cuotas_pactadas(class="form-control" + (" is-invalid" if form.cuotas_pactadas.errors else "")) }}
97
+ {% if form.cuotas_pactadas.errors %}
98
+ <div class="invalid-feedback">{{ form.cuotas_pactadas.errors[0] }}</div>
99
+ {% endif %}
100
+ {% if form.cuotas_pactadas.description %}
101
+ <div class="form-text">{{ form.cuotas_pactadas.description }}</div>
102
+ {% endif %}
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Interest Section (for loans) -->
107
+ <div class="row mb-3">
108
+ <div class="col-md-4">
109
+ <label for="{{ form.tasa_interes.id }}" class="form-label">
110
+ {{ form.tasa_interes.label.text }}
111
+ </label>
112
+ {{ form.tasa_interes(class="form-control" + (" is-invalid" if form.tasa_interes.errors else "")) }}
113
+ {% if form.tasa_interes.errors %}
114
+ <div class="invalid-feedback">{{ form.tasa_interes.errors[0] }}</div>
115
+ {% endif %}
116
+ {% if form.tasa_interes.description %}
117
+ <div class="form-text">{{ form.tasa_interes.description }}</div>
118
+ {% endif %}
119
+ </div>
120
+
121
+ <div class="col-md-4">
122
+ <label for="{{ form.tipo_interes.id }}" class="form-label">
123
+ {{ form.tipo_interes.label.text }}
124
+ </label>
125
+ {{ form.tipo_interes(class="form-select" + (" is-invalid" if form.tipo_interes.errors else "")) }}
126
+ {% if form.tipo_interes.errors %}
127
+ <div class="invalid-feedback">{{ form.tipo_interes.errors[0] }}</div>
128
+ {% endif %}
129
+ {% if form.tipo_interes.description %}
130
+ <div class="form-text">{{ form.tipo_interes.description }}</div>
131
+ {% endif %}
132
+ </div>
133
+
134
+ <div class="col-md-4">
135
+ <label for="{{ form.deduccion_id.id }}" class="form-label">
136
+ {{ form.deduccion_id.label.text }}
137
+ </label>
138
+ {{ form.deduccion_id(class="form-select" + (" is-invalid" if form.deduccion_id.errors else "")) }}
139
+ {% if form.deduccion_id.errors %}
140
+ <div class="invalid-feedback">{{ form.deduccion_id.errors[0] }}</div>
141
+ {% endif %}
142
+ {% if form.deduccion_id.description %}
143
+ <div class="form-text">{{ form.deduccion_id.description }}</div>
144
+ {% endif %}
145
+ </div>
146
+ </div>
147
+
148
+ <!-- Accounting Section -->
149
+ <div class="row mb-3">
150
+ <div class="col-md-6">
151
+ <label for="{{ form.cuenta_debe.id }}" class="form-label">
152
+ {{ form.cuenta_debe.label.text }}
153
+ </label>
154
+ {{ form.cuenta_debe(class="form-control" + (" is-invalid" if form.cuenta_debe.errors else "")) }}
155
+ {% if form.cuenta_debe.errors %}
156
+ <div class="invalid-feedback">{{ form.cuenta_debe.errors[0] }}</div>
157
+ {% endif %}
158
+ {% if form.cuenta_debe.description %}
159
+ <div class="form-text">{{ form.cuenta_debe.description }}</div>
160
+ {% endif %}
161
+ </div>
162
+
163
+ <div class="col-md-6">
164
+ <label for="{{ form.cuenta_haber.id }}" class="form-label">
165
+ {{ form.cuenta_haber.label.text }}
166
+ </label>
167
+ {{ form.cuenta_haber(class="form-control" + (" is-invalid" if form.cuenta_haber.errors else "")) }}
168
+ {% if form.cuenta_haber.errors %}
169
+ <div class="invalid-feedback">{{ form.cuenta_haber.errors[0] }}</div>
170
+ {% endif %}
171
+ {% if form.cuenta_haber.description %}
172
+ <div class="form-text">{{ form.cuenta_haber.description }}</div>
173
+ {% endif %}
174
+ </div>
175
+ </div>
176
+
177
+ <!-- Reason -->
178
+ <div class="row mb-3">
179
+ <div class="col-12">
180
+ <label for="{{ form.motivo.id }}" class="form-label">
181
+ {{ form.motivo.label.text }}
182
+ </label>
183
+ {{ form.motivo(class="form-control" + (" is-invalid" if form.motivo.errors else ""), rows=3) }}
184
+ {% if form.motivo.errors %}
185
+ <div class="invalid-feedback">{{ form.motivo.errors[0] }}</div>
186
+ {% endif %}
187
+ {% if form.motivo.description %}
188
+ <div class="form-text">{{ form.motivo.description }}</div>
189
+ {% endif %}
190
+ </div>
191
+ </div>
192
+
193
+ <div class="d-flex justify-content-end gap-2">
194
+ <a href="{{ url_for('prestamo.index') }}" class="btn btn-secondary">
195
+ {{ _('Cancelar') }}
196
+ </a>
197
+ {{ form.submit(class="btn btn-primary") }}
198
+ </div>
199
+ </form>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ {% endblock %}