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.
- coati_payroll/__init__.py +415 -0
- coati_payroll/app.py +95 -0
- coati_payroll/audit_helpers.py +904 -0
- coati_payroll/auth.py +123 -0
- coati_payroll/cli.py +1318 -0
- coati_payroll/config.py +219 -0
- coati_payroll/demo_data.py +813 -0
- coati_payroll/enums.py +278 -0
- coati_payroll/forms.py +1769 -0
- coati_payroll/formula_engine/__init__.py +81 -0
- coati_payroll/formula_engine/ast/__init__.py +110 -0
- coati_payroll/formula_engine/ast/ast_visitor.py +259 -0
- coati_payroll/formula_engine/ast/expression_evaluator.py +228 -0
- coati_payroll/formula_engine/ast/safe_operators.py +131 -0
- coati_payroll/formula_engine/ast/type_converter.py +172 -0
- coati_payroll/formula_engine/data_sources.py +752 -0
- coati_payroll/formula_engine/engine.py +247 -0
- coati_payroll/formula_engine/exceptions.py +52 -0
- coati_payroll/formula_engine/execution/__init__.py +24 -0
- coati_payroll/formula_engine/execution/execution_context.py +52 -0
- coati_payroll/formula_engine/execution/step_executor.py +62 -0
- coati_payroll/formula_engine/execution/variable_store.py +59 -0
- coati_payroll/formula_engine/novelty_codes.py +206 -0
- coati_payroll/formula_engine/results/__init__.py +20 -0
- coati_payroll/formula_engine/results/execution_result.py +59 -0
- coati_payroll/formula_engine/steps/__init__.py +30 -0
- coati_payroll/formula_engine/steps/assignment_step.py +71 -0
- coati_payroll/formula_engine/steps/base_step.py +48 -0
- coati_payroll/formula_engine/steps/calculation_step.py +42 -0
- coati_payroll/formula_engine/steps/conditional_step.py +122 -0
- coati_payroll/formula_engine/steps/step_factory.py +58 -0
- coati_payroll/formula_engine/steps/tax_lookup_step.py +45 -0
- coati_payroll/formula_engine/tables/__init__.py +24 -0
- coati_payroll/formula_engine/tables/bracket_calculator.py +51 -0
- coati_payroll/formula_engine/tables/table_lookup.py +161 -0
- coati_payroll/formula_engine/tables/tax_table.py +32 -0
- coati_payroll/formula_engine/validation/__init__.py +24 -0
- coati_payroll/formula_engine/validation/schema_validator.py +37 -0
- coati_payroll/formula_engine/validation/security_validator.py +52 -0
- coati_payroll/formula_engine/validation/tax_table_validator.py +205 -0
- coati_payroll/formula_engine_examples.py +153 -0
- coati_payroll/i18n.py +54 -0
- coati_payroll/initial_data.py +613 -0
- coati_payroll/interes_engine.py +450 -0
- coati_payroll/liquidacion_engine/__init__.py +25 -0
- coati_payroll/liquidacion_engine/engine.py +267 -0
- coati_payroll/locale_config.py +165 -0
- coati_payroll/log.py +138 -0
- coati_payroll/model.py +2410 -0
- coati_payroll/nomina_engine/__init__.py +87 -0
- coati_payroll/nomina_engine/calculators/__init__.py +30 -0
- coati_payroll/nomina_engine/calculators/benefit_calculator.py +79 -0
- coati_payroll/nomina_engine/calculators/concept_calculator.py +254 -0
- coati_payroll/nomina_engine/calculators/deduction_calculator.py +105 -0
- coati_payroll/nomina_engine/calculators/exchange_rate_calculator.py +51 -0
- coati_payroll/nomina_engine/calculators/perception_calculator.py +75 -0
- coati_payroll/nomina_engine/calculators/salary_calculator.py +86 -0
- coati_payroll/nomina_engine/domain/__init__.py +27 -0
- coati_payroll/nomina_engine/domain/calculation_items.py +52 -0
- coati_payroll/nomina_engine/domain/employee_calculation.py +53 -0
- coati_payroll/nomina_engine/domain/payroll_context.py +44 -0
- coati_payroll/nomina_engine/engine.py +188 -0
- coati_payroll/nomina_engine/processors/__init__.py +28 -0
- coati_payroll/nomina_engine/processors/accounting_processor.py +171 -0
- coati_payroll/nomina_engine/processors/accumulation_processor.py +90 -0
- coati_payroll/nomina_engine/processors/loan_processor.py +227 -0
- coati_payroll/nomina_engine/processors/novelty_processor.py +42 -0
- coati_payroll/nomina_engine/processors/vacation_processor.py +67 -0
- coati_payroll/nomina_engine/repositories/__init__.py +32 -0
- coati_payroll/nomina_engine/repositories/acumulado_repository.py +83 -0
- coati_payroll/nomina_engine/repositories/base_repository.py +40 -0
- coati_payroll/nomina_engine/repositories/config_repository.py +102 -0
- coati_payroll/nomina_engine/repositories/employee_repository.py +34 -0
- coati_payroll/nomina_engine/repositories/exchange_rate_repository.py +58 -0
- coati_payroll/nomina_engine/repositories/novelty_repository.py +54 -0
- coati_payroll/nomina_engine/repositories/planilla_repository.py +52 -0
- coati_payroll/nomina_engine/results/__init__.py +24 -0
- coati_payroll/nomina_engine/results/error_result.py +28 -0
- coati_payroll/nomina_engine/results/payroll_result.py +53 -0
- coati_payroll/nomina_engine/results/validation_result.py +39 -0
- coati_payroll/nomina_engine/services/__init__.py +22 -0
- coati_payroll/nomina_engine/services/accounting_voucher_service.py +708 -0
- coati_payroll/nomina_engine/services/employee_processing_service.py +173 -0
- coati_payroll/nomina_engine/services/payroll_execution_service.py +374 -0
- coati_payroll/nomina_engine/services/snapshot_service.py +295 -0
- coati_payroll/nomina_engine/validators/__init__.py +31 -0
- coati_payroll/nomina_engine/validators/base_validator.py +48 -0
- coati_payroll/nomina_engine/validators/currency_validator.py +50 -0
- coati_payroll/nomina_engine/validators/employee_validator.py +87 -0
- coati_payroll/nomina_engine/validators/period_validator.py +44 -0
- coati_payroll/nomina_engine/validators/planilla_validator.py +136 -0
- coati_payroll/plugin_manager.py +176 -0
- coati_payroll/queue/__init__.py +33 -0
- coati_payroll/queue/driver.py +127 -0
- coati_payroll/queue/drivers/__init__.py +22 -0
- coati_payroll/queue/drivers/dramatiq_driver.py +268 -0
- coati_payroll/queue/drivers/huey_driver.py +390 -0
- coati_payroll/queue/drivers/noop_driver.py +54 -0
- coati_payroll/queue/selector.py +121 -0
- coati_payroll/queue/tasks.py +764 -0
- coati_payroll/rate_limiting.py +83 -0
- coati_payroll/rbac.py +183 -0
- coati_payroll/report_engine.py +512 -0
- coati_payroll/report_export.py +208 -0
- coati_payroll/schema_validator.py +167 -0
- coati_payroll/security.py +77 -0
- coati_payroll/static/styles.css +1044 -0
- coati_payroll/system_reports.py +573 -0
- coati_payroll/templates/auth/login.html +189 -0
- coati_payroll/templates/base.html +283 -0
- coati_payroll/templates/index.html +227 -0
- coati_payroll/templates/macros.html +146 -0
- coati_payroll/templates/modules/calculation_rule/form.html +78 -0
- coati_payroll/templates/modules/calculation_rule/index.html +102 -0
- coati_payroll/templates/modules/calculation_rule/schema_editor.html +1159 -0
- coati_payroll/templates/modules/carga_inicial_prestacion/form.html +170 -0
- coati_payroll/templates/modules/carga_inicial_prestacion/index.html +170 -0
- coati_payroll/templates/modules/carga_inicial_prestacion/reporte.html +193 -0
- coati_payroll/templates/modules/config_calculos/index.html +44 -0
- coati_payroll/templates/modules/configuracion/index.html +90 -0
- coati_payroll/templates/modules/currency/form.html +47 -0
- coati_payroll/templates/modules/currency/index.html +64 -0
- coati_payroll/templates/modules/custom_field/form.html +62 -0
- coati_payroll/templates/modules/custom_field/index.html +78 -0
- coati_payroll/templates/modules/deduccion/form.html +1 -0
- coati_payroll/templates/modules/deduccion/index.html +1 -0
- coati_payroll/templates/modules/employee/form.html +254 -0
- coati_payroll/templates/modules/employee/index.html +76 -0
- coati_payroll/templates/modules/empresa/form.html +74 -0
- coati_payroll/templates/modules/empresa/index.html +71 -0
- coati_payroll/templates/modules/exchange_rate/form.html +47 -0
- coati_payroll/templates/modules/exchange_rate/import.html +93 -0
- coati_payroll/templates/modules/exchange_rate/index.html +114 -0
- coati_payroll/templates/modules/liquidacion/index.html +58 -0
- coati_payroll/templates/modules/liquidacion/nueva.html +51 -0
- coati_payroll/templates/modules/liquidacion/ver.html +91 -0
- coati_payroll/templates/modules/payroll_concepts/audit_log.html +146 -0
- coati_payroll/templates/modules/percepcion/form.html +1 -0
- coati_payroll/templates/modules/percepcion/index.html +1 -0
- coati_payroll/templates/modules/planilla/config.html +190 -0
- coati_payroll/templates/modules/planilla/config_deducciones.html +129 -0
- coati_payroll/templates/modules/planilla/config_empleados.html +116 -0
- coati_payroll/templates/modules/planilla/config_percepciones.html +113 -0
- coati_payroll/templates/modules/planilla/config_prestaciones.html +118 -0
- coati_payroll/templates/modules/planilla/config_reglas.html +120 -0
- coati_payroll/templates/modules/planilla/ejecutar_nomina.html +106 -0
- coati_payroll/templates/modules/planilla/form.html +197 -0
- coati_payroll/templates/modules/planilla/index.html +144 -0
- coati_payroll/templates/modules/planilla/listar_nominas.html +91 -0
- coati_payroll/templates/modules/planilla/log_nomina.html +135 -0
- coati_payroll/templates/modules/planilla/novedades/form.html +177 -0
- coati_payroll/templates/modules/planilla/novedades/index.html +170 -0
- coati_payroll/templates/modules/planilla/ver_nomina.html +477 -0
- coati_payroll/templates/modules/planilla/ver_nomina_empleado.html +231 -0
- coati_payroll/templates/modules/plugins/index.html +71 -0
- coati_payroll/templates/modules/prestacion/form.html +1 -0
- coati_payroll/templates/modules/prestacion/index.html +1 -0
- coati_payroll/templates/modules/prestacion_management/dashboard.html +150 -0
- coati_payroll/templates/modules/prestacion_management/initial_balance_bulk.html +195 -0
- coati_payroll/templates/modules/prestamo/approve.html +156 -0
- coati_payroll/templates/modules/prestamo/condonacion.html +249 -0
- coati_payroll/templates/modules/prestamo/detail.html +443 -0
- coati_payroll/templates/modules/prestamo/form.html +203 -0
- coati_payroll/templates/modules/prestamo/index.html +150 -0
- coati_payroll/templates/modules/prestamo/pago_extraordinario.html +211 -0
- coati_payroll/templates/modules/prestamo/tabla_pago_pdf.html +181 -0
- coati_payroll/templates/modules/report/admin_index.html +125 -0
- coati_payroll/templates/modules/report/detail.html +129 -0
- coati_payroll/templates/modules/report/execute.html +266 -0
- coati_payroll/templates/modules/report/index.html +95 -0
- coati_payroll/templates/modules/report/permissions.html +64 -0
- coati_payroll/templates/modules/settings/index.html +274 -0
- coati_payroll/templates/modules/shared/concept_form.html +201 -0
- coati_payroll/templates/modules/shared/concept_index.html +145 -0
- coati_payroll/templates/modules/tipo_planilla/form.html +70 -0
- coati_payroll/templates/modules/tipo_planilla/index.html +68 -0
- coati_payroll/templates/modules/user/form.html +65 -0
- coati_payroll/templates/modules/user/index.html +76 -0
- coati_payroll/templates/modules/user/profile.html +81 -0
- coati_payroll/templates/modules/vacation/account_detail.html +149 -0
- coati_payroll/templates/modules/vacation/account_form.html +52 -0
- coati_payroll/templates/modules/vacation/account_index.html +68 -0
- coati_payroll/templates/modules/vacation/dashboard.html +156 -0
- coati_payroll/templates/modules/vacation/initial_balance_bulk.html +149 -0
- coati_payroll/templates/modules/vacation/initial_balance_form.html +93 -0
- coati_payroll/templates/modules/vacation/leave_request_detail.html +158 -0
- coati_payroll/templates/modules/vacation/leave_request_form.html +61 -0
- coati_payroll/templates/modules/vacation/leave_request_index.html +98 -0
- coati_payroll/templates/modules/vacation/policy_detail.html +176 -0
- coati_payroll/templates/modules/vacation/policy_form.html +152 -0
- coati_payroll/templates/modules/vacation/policy_index.html +79 -0
- coati_payroll/templates/modules/vacation/register_taken_form.html +178 -0
- coati_payroll/translations/en/LC_MESSAGES/messages.mo +0 -0
- coati_payroll/translations/en/LC_MESSAGES/messages.po +7283 -0
- coati_payroll/translations/es/LC_MESSAGES/messages.mo +0 -0
- coati_payroll/translations/es/LC_MESSAGES/messages.po +7374 -0
- coati_payroll/vacation_service.py +451 -0
- coati_payroll/version.py +18 -0
- coati_payroll/vistas/__init__.py +64 -0
- coati_payroll/vistas/calculation_rule.py +307 -0
- coati_payroll/vistas/carga_inicial_prestacion.py +423 -0
- coati_payroll/vistas/config_calculos.py +72 -0
- coati_payroll/vistas/configuracion.py +87 -0
- coati_payroll/vistas/constants.py +17 -0
- coati_payroll/vistas/currency.py +112 -0
- coati_payroll/vistas/custom_field.py +120 -0
- coati_payroll/vistas/employee.py +305 -0
- coati_payroll/vistas/empresa.py +153 -0
- coati_payroll/vistas/exchange_rate.py +341 -0
- coati_payroll/vistas/liquidacion.py +205 -0
- coati_payroll/vistas/payroll_concepts.py +580 -0
- coati_payroll/vistas/planilla/__init__.py +38 -0
- coati_payroll/vistas/planilla/association_routes.py +238 -0
- coati_payroll/vistas/planilla/config_routes.py +158 -0
- coati_payroll/vistas/planilla/export_routes.py +175 -0
- coati_payroll/vistas/planilla/helpers/__init__.py +34 -0
- coati_payroll/vistas/planilla/helpers/association_helpers.py +161 -0
- coati_payroll/vistas/planilla/helpers/excel_helpers.py +29 -0
- coati_payroll/vistas/planilla/helpers/form_helpers.py +97 -0
- coati_payroll/vistas/planilla/nomina_routes.py +488 -0
- coati_payroll/vistas/planilla/novedad_routes.py +227 -0
- coati_payroll/vistas/planilla/routes.py +145 -0
- coati_payroll/vistas/planilla/services/__init__.py +26 -0
- coati_payroll/vistas/planilla/services/export_service.py +687 -0
- coati_payroll/vistas/planilla/services/nomina_service.py +233 -0
- coati_payroll/vistas/planilla/services/novedad_service.py +126 -0
- coati_payroll/vistas/planilla/services/planilla_service.py +34 -0
- coati_payroll/vistas/planilla/validators/__init__.py +18 -0
- coati_payroll/vistas/planilla/validators/planilla_validators.py +40 -0
- coati_payroll/vistas/plugins.py +45 -0
- coati_payroll/vistas/prestacion.py +272 -0
- coati_payroll/vistas/prestamo.py +808 -0
- coati_payroll/vistas/report.py +432 -0
- coati_payroll/vistas/settings.py +29 -0
- coati_payroll/vistas/tipo_planilla.py +134 -0
- coati_payroll/vistas/user.py +172 -0
- coati_payroll/vistas/vacation.py +1045 -0
- coati_payroll-0.0.2.dist-info/LICENSE +201 -0
- coati_payroll-0.0.2.dist-info/METADATA +581 -0
- coati_payroll-0.0.2.dist-info/RECORD +243 -0
- coati_payroll-0.0.2.dist-info/WHEEL +5 -0
- coati_payroll-0.0.2.dist-info/entry_points.txt +2 -0
- coati_payroll-0.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
{% block content %}
|
|
3
|
+
<div class="content-header">
|
|
4
|
+
<h3>
|
|
5
|
+
Nómina: {{ nomina.periodo_inicio.strftime('%d/%m/%Y') }} - {{ nomina.periodo_fin.strftime('%d/%m/%Y') }}
|
|
6
|
+
</h3>
|
|
7
|
+
<div>
|
|
8
|
+
<a href="{{ url_for('planilla.listar_nominas', planilla_id=planilla.id) }}" class="btn btn-secondary">
|
|
9
|
+
<i class="fas fa-arrow-left"></i> Volver al Historial
|
|
10
|
+
</a>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
{% if has_errors %}
|
|
15
|
+
<div class="alert alert-danger mb-4">
|
|
16
|
+
<h5><i class="fas fa-times-circle"></i> Esta Nómina Tiene Errores de Procesamiento</h5>
|
|
17
|
+
<p class="mb-2">No se puede aprobar ni aplicar esta nómina hasta que se corrijan los siguientes errores:</p>
|
|
18
|
+
<ul class="mb-2">
|
|
19
|
+
{% for error in error_messages %}
|
|
20
|
+
<li>{{ error }}</li>
|
|
21
|
+
{% endfor %}
|
|
22
|
+
</ul>
|
|
23
|
+
<p class="mb-0">
|
|
24
|
+
<strong>Acción requerida:</strong> Corrija los datos (tipo de cambio, configuración, etc.) y recalcule la nómina.
|
|
25
|
+
<a href="{{ url_for('planilla.ver_log_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}" class="alert-link">Ver log completo</a>
|
|
26
|
+
</p>
|
|
27
|
+
</div>
|
|
28
|
+
{% elif has_warnings %}
|
|
29
|
+
<div class="alert alert-warning mb-4">
|
|
30
|
+
<h5><i class="fas fa-exclamation-triangle"></i> Esta Nómina Tiene Advertencias</h5>
|
|
31
|
+
<p class="mb-2">La nómina se procesó pero hay situaciones que requieren su atención:</p>
|
|
32
|
+
<ul class="mb-2">
|
|
33
|
+
{% for warning in warning_messages %}
|
|
34
|
+
<li>{{ warning }}</li>
|
|
35
|
+
{% endfor %}
|
|
36
|
+
</ul>
|
|
37
|
+
<p class="mb-0">
|
|
38
|
+
<a href="{{ url_for('planilla.ver_log_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}" class="alert-link">Ver log completo</a>
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
{% endif %}
|
|
42
|
+
|
|
43
|
+
{% if nomina.estado == 'calculando' %}
|
|
44
|
+
<div class="card mb-4 border-primary">
|
|
45
|
+
<div class="card-header bg-primary text-white">
|
|
46
|
+
<h5 class="mb-0">
|
|
47
|
+
<i class="fas fa-spinner fa-spin"></i> Calculando Nómina en Segundo Plano
|
|
48
|
+
</h5>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="card-body">
|
|
51
|
+
<p class="mb-3">
|
|
52
|
+
<strong>La nómina está siendo calculada en segundo plano.</strong>
|
|
53
|
+
El sistema no se ha colgado, por favor espere mientras se procesan todos los empleados.
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
<!-- Current employee being processed -->
|
|
57
|
+
<div class="alert alert-info mb-3" id="current-employee-alert">
|
|
58
|
+
<i class="fas fa-user-clock"></i>
|
|
59
|
+
<strong>Procesando:</strong>
|
|
60
|
+
<span id="empleado-actual">{{ nomina.empleado_actual or 'Iniciando...' }}</span>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Progress bar -->
|
|
64
|
+
<div class="progress mb-3" style="height: 30px;">
|
|
65
|
+
<div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated bg-primary"
|
|
66
|
+
role="progressbar"
|
|
67
|
+
style="width: {% if nomina.total_empleados and nomina.total_empleados > 0 %}{{ (nomina.empleados_procesados / nomina.total_empleados * 100)|int }}%{% else %}0%{% endif %}"
|
|
68
|
+
aria-valuenow="{{ nomina.empleados_procesados or 0 }}"
|
|
69
|
+
aria-valuemin="0"
|
|
70
|
+
aria-valuemax="{{ nomina.total_empleados or 100 }}">
|
|
71
|
+
<span id="progress-text">
|
|
72
|
+
{% if nomina.total_empleados and nomina.total_empleados > 0 %}
|
|
73
|
+
{{ (nomina.empleados_procesados / nomina.total_empleados * 100)|int }}%
|
|
74
|
+
{% else %}
|
|
75
|
+
0%
|
|
76
|
+
{% endif %}
|
|
77
|
+
({{ nomina.empleados_procesados or 0 }}/{{ nomina.total_empleados or 0 }})
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Statistics -->
|
|
83
|
+
<div class="row text-center mb-3">
|
|
84
|
+
<div class="col-md-4">
|
|
85
|
+
<h6 class="text-muted">Total Empleados</h6>
|
|
86
|
+
<h4 id="total-empleados">{{ nomina.total_empleados or 0 }}</h4>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="col-md-4">
|
|
89
|
+
<h6 class="text-muted">Procesados</h6>
|
|
90
|
+
<h4 id="empleados-procesados" class="text-success">{{ nomina.empleados_procesados or 0 }}</h4>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="col-md-4">
|
|
93
|
+
<h6 class="text-muted">Con Errores</h6>
|
|
94
|
+
<h4 id="empleados-con-error" class="text-danger">{{ nomina.empleados_con_error or 0 }}</h4>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Activity Log -->
|
|
99
|
+
<div class="card">
|
|
100
|
+
<div class="card-header bg-light">
|
|
101
|
+
<h6 class="mb-0">
|
|
102
|
+
<i class="fas fa-list"></i> Registro de Actividad
|
|
103
|
+
<button class="btn btn-sm btn-outline-secondary float-end" onclick="scrollLogToBottom()">
|
|
104
|
+
<i class="fas fa-arrow-down"></i> Ir al final
|
|
105
|
+
</button>
|
|
106
|
+
</h6>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="card-body" style="max-height: 300px; overflow-y: auto; font-family: monospace; font-size: 0.9em; background-color: #f8f9fa;" id="activity-log">
|
|
109
|
+
{% if nomina.log_procesamiento %}
|
|
110
|
+
{% for entry in nomina.log_procesamiento %}
|
|
111
|
+
<div class="log-entry {% if entry.status == 'success' %}text-success{% elif entry.status == 'error' %}text-danger{% elif entry.status == 'warning' %}text-warning{% else %}text-muted{% endif %}">
|
|
112
|
+
{% if entry.status == 'error' %}<i class="fas fa-times-circle"></i> {% elif entry.status == 'warning' %}<i class="fas fa-exclamation-triangle"></i> {% endif %}{{ entry.message }}
|
|
113
|
+
</div>
|
|
114
|
+
{% endfor %}
|
|
115
|
+
{% else %}
|
|
116
|
+
<div class="text-muted">{{ _('No hay mensajes en el log de procesamiento para esta nómina.') }}</div>
|
|
117
|
+
{% endif %}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="text-center mt-3">
|
|
122
|
+
<small class="text-muted">
|
|
123
|
+
<i class="fas fa-sync-alt fa-spin"></i>
|
|
124
|
+
Esta página se actualiza automáticamente cada 3 segundos
|
|
125
|
+
</small>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
{% elif nomina.estado == 'error' %}
|
|
130
|
+
<div class="alert alert-danger">
|
|
131
|
+
<h5><i class="fas fa-exclamation-triangle"></i> Error en el Cálculo de la Nómina</h5>
|
|
132
|
+
<p>Ocurrió un error durante el cálculo de la nómina. Detalles:</p>
|
|
133
|
+
{% if nomina.errores_calculo %}
|
|
134
|
+
<ul>
|
|
135
|
+
{% for key, error in nomina.errores_calculo.items() %}
|
|
136
|
+
<li>
|
|
137
|
+
<strong>{{ key }}:</strong>
|
|
138
|
+
{% if error is mapping %}
|
|
139
|
+
{{ error.critical_error or error.error or error }}
|
|
140
|
+
{% else %}
|
|
141
|
+
{{ error }}
|
|
142
|
+
{% endif %}
|
|
143
|
+
</li>
|
|
144
|
+
{% endfor %}
|
|
145
|
+
</ul>
|
|
146
|
+
{% endif %}
|
|
147
|
+
<p class="mb-3">
|
|
148
|
+
<strong>Empleados procesados:</strong> {{ nomina.empleados_procesados or 0 }} / {{ nomina.total_empleados or 0 }}<br>
|
|
149
|
+
<strong>Empleados con error:</strong> {{ nomina.empleados_con_error or 0 }}
|
|
150
|
+
</p>
|
|
151
|
+
<div>
|
|
152
|
+
<form action="{{ url_for('planilla.reintentar_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}"
|
|
153
|
+
method="post" style="display: inline;">
|
|
154
|
+
<button type="submit" class="btn btn-warning"
|
|
155
|
+
onclick="return confirm('¿Reintentar el procesamiento de esta nómina? Se limpiarán los datos parciales y se intentará procesar nuevamente.')">
|
|
156
|
+
<i class="fas fa-redo"></i> Reintentar Procesamiento
|
|
157
|
+
</button>
|
|
158
|
+
</form>
|
|
159
|
+
{% if nomina.errores_calculo and nomina.errores_calculo.get('is_recoverable') == False %}
|
|
160
|
+
<small class="text-muted d-block mt-2">
|
|
161
|
+
<i class="fas fa-info-circle"></i>
|
|
162
|
+
Este error no es recuperable automáticamente. Revise la configuración o los datos antes de reintentar.
|
|
163
|
+
</small>
|
|
164
|
+
{% endif %}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
{% endif %}
|
|
168
|
+
|
|
169
|
+
{% if nomina.estado == 'generado' and nomina.empleados_con_error and nomina.empleados_con_error > 0 %}
|
|
170
|
+
<div class="alert alert-warning">
|
|
171
|
+
<h5><i class="fas fa-exclamation-circle"></i> Advertencia: Algunos Empleados No Se Procesaron Correctamente</h5>
|
|
172
|
+
<p>La nómina se generó exitosamente, pero {{ nomina.empleados_con_error }} empleado(s) tuvieron errores durante el cálculo.</p>
|
|
173
|
+
{% if nomina.errores_calculo %}
|
|
174
|
+
<details>
|
|
175
|
+
<summary><strong>Ver errores</strong></summary>
|
|
176
|
+
<ul class="mt-2">
|
|
177
|
+
{% for empleado_id, error in nomina.errores_calculo.items() %}
|
|
178
|
+
<li><strong>{{ empleado_id }}:</strong> {{ error }}</li>
|
|
179
|
+
{% endfor %}
|
|
180
|
+
</ul>
|
|
181
|
+
</details>
|
|
182
|
+
{% endif %}
|
|
183
|
+
</div>
|
|
184
|
+
{% endif %}
|
|
185
|
+
|
|
186
|
+
<div class="card mb-4">
|
|
187
|
+
<div class="card-header">
|
|
188
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
189
|
+
<h5 class="mb-0">Resumen de la Nómina</h5>
|
|
190
|
+
<div>
|
|
191
|
+
{% if nomina.estado == 'generado' %}
|
|
192
|
+
<form action="{{ url_for('planilla.aprobar_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}"
|
|
193
|
+
method="post" style="display: inline;">
|
|
194
|
+
{% if has_errors %}
|
|
195
|
+
<button type="button" class="btn btn-success" disabled title="No se puede aprobar una nómina con errores de procesamiento">
|
|
196
|
+
<i class="fas fa-check"></i> Aprobar
|
|
197
|
+
</button>
|
|
198
|
+
{% else %}
|
|
199
|
+
<button type="submit" class="btn btn-success" onclick="return confirm('¿Aprobar esta nómina?')">
|
|
200
|
+
<i class="fas fa-check"></i> Aprobar
|
|
201
|
+
</button>
|
|
202
|
+
{% endif %}
|
|
203
|
+
</form>
|
|
204
|
+
{% elif nomina.estado == 'aprobado' %}
|
|
205
|
+
<form action="{{ url_for('planilla.aplicar_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}"
|
|
206
|
+
method="post" style="display: inline;">
|
|
207
|
+
<button type="submit" class="btn btn-primary" onclick="return confirm('¿Marcar como aplicada/pagada?')">
|
|
208
|
+
<i class="fas fa-money-bill"></i> Aplicar/Pagar
|
|
209
|
+
</button>
|
|
210
|
+
</form>
|
|
211
|
+
{% endif %}
|
|
212
|
+
{% if nomina.estado != 'aplicado' %}
|
|
213
|
+
<form action="{{ url_for('planilla.recalcular_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}"
|
|
214
|
+
method="post" style="display: inline;">
|
|
215
|
+
<button type="submit" class="btn btn-warning" onclick="return confirm('¿Recalcular esta nómina? Se eliminarán los datos actuales y se generarán nuevamente.')">
|
|
216
|
+
<i class="fas fa-sync-alt"></i> Recalcular
|
|
217
|
+
</button>
|
|
218
|
+
</form>
|
|
219
|
+
{% endif %}
|
|
220
|
+
<a href="{{ url_for('planilla.listar_novedades', planilla_id=planilla.id, nomina_id=nomina.id) }}" class="btn btn-info">
|
|
221
|
+
<i class="bi bi-list-check"></i> Novedades
|
|
222
|
+
</a>
|
|
223
|
+
<a href="{{ url_for('planilla.ver_log_nomina', planilla_id=planilla.id, nomina_id=nomina.id) }}" class="btn btn-outline-secondary">
|
|
224
|
+
<i class="fas fa-list-alt"></i> Log de Ejecución
|
|
225
|
+
</a>
|
|
226
|
+
{% if nomina.estado in ['generado', 'aprobado', 'aplicado'] %}
|
|
227
|
+
<div class="btn-group" role="group">
|
|
228
|
+
<button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
|
229
|
+
<i class="fas fa-file-excel"></i> Exportar
|
|
230
|
+
</button>
|
|
231
|
+
<ul class="dropdown-menu">
|
|
232
|
+
<li>
|
|
233
|
+
<a class="dropdown-item" href="{{ url_for('planilla.exportar_nomina_excel', planilla_id=planilla.id, nomina_id=nomina.id) }}">
|
|
234
|
+
<i class="fas fa-file-excel"></i> Nómina (Excel)
|
|
235
|
+
</a>
|
|
236
|
+
</li>
|
|
237
|
+
<li>
|
|
238
|
+
<a class="dropdown-item" href="{{ url_for('planilla.exportar_prestaciones_excel', planilla_id=planilla.id, nomina_id=nomina.id) }}">
|
|
239
|
+
<i class="fas fa-file-excel"></i> Prestaciones (Excel)
|
|
240
|
+
</a>
|
|
241
|
+
</li>
|
|
242
|
+
<li>
|
|
243
|
+
<a class="dropdown-item" href="{{ url_for('planilla.exportar_comprobante_excel', planilla_id=planilla.id, nomina_id=nomina.id) }}">
|
|
244
|
+
<i class="fas fa-file-invoice"></i> Comprobante Contable (Excel)
|
|
245
|
+
</a>
|
|
246
|
+
</li>
|
|
247
|
+
<li>
|
|
248
|
+
<a class="dropdown-item" href="{{ url_for('planilla.exportar_comprobante_detallado_excel', planilla_id=planilla.id, nomina_id=nomina.id) }}">
|
|
249
|
+
<i class="fas fa-list-alt"></i> Comprobante Detallado por Empleado (Excel)
|
|
250
|
+
</a>
|
|
251
|
+
</li>
|
|
252
|
+
</ul>
|
|
253
|
+
</div>
|
|
254
|
+
{% endif %}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="card-body">
|
|
259
|
+
<div class="row">
|
|
260
|
+
<div class="col-md-3">
|
|
261
|
+
<strong>Planilla:</strong><br>
|
|
262
|
+
{{ planilla.nombre }}
|
|
263
|
+
</div>
|
|
264
|
+
<div class="col-md-2">
|
|
265
|
+
<strong>Estado:</strong><br>
|
|
266
|
+
{% if nomina.estado == 'calculando' %}
|
|
267
|
+
<span class="badge bg-primary"><i class="fas fa-spinner fa-spin"></i> Calculando</span>
|
|
268
|
+
{% elif nomina.estado == 'generado' %}
|
|
269
|
+
<span class="badge bg-warning">Generado</span>
|
|
270
|
+
{% elif nomina.estado == 'aprobado' %}
|
|
271
|
+
<span class="badge bg-info">Aprobado</span>
|
|
272
|
+
{% elif nomina.estado == 'aplicado' %}
|
|
273
|
+
<span class="badge bg-success">Aplicado</span>
|
|
274
|
+
{% elif nomina.estado == 'error' %}
|
|
275
|
+
<span class="badge bg-danger">Error</span>
|
|
276
|
+
{% endif %}
|
|
277
|
+
</div>
|
|
278
|
+
<div class="col-md-2">
|
|
279
|
+
<strong>Moneda:</strong><br>
|
|
280
|
+
{{ planilla.moneda.codigo if planilla.moneda else 'N/A' }}
|
|
281
|
+
</div>
|
|
282
|
+
<div class="col-md-2">
|
|
283
|
+
<strong>Empleados:</strong><br>
|
|
284
|
+
{{ nomina_empleados|length }}
|
|
285
|
+
</div>
|
|
286
|
+
<div class="col-md-3">
|
|
287
|
+
<strong>Generado:</strong><br>
|
|
288
|
+
{{ nomina.fecha_generacion.strftime('%d/%m/%Y %H:%M') }}
|
|
289
|
+
{% if nomina.generado_por %}por {{ nomina.generado_por }}{% endif %}
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
292
|
+
<hr>
|
|
293
|
+
<div class="row">
|
|
294
|
+
<div class="col-md-4">
|
|
295
|
+
<div class="card bg-light">
|
|
296
|
+
<div class="card-body text-center">
|
|
297
|
+
<h6 class="text-muted mb-1">Total Bruto</h6>
|
|
298
|
+
<h4 class="text-primary mb-0">{{ planilla.moneda.simbolo if planilla.moneda else '' }} {{ "{:,.2f}".format(nomina.total_bruto|float) }}</h4>
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
<div class="col-md-4">
|
|
303
|
+
<div class="card bg-light">
|
|
304
|
+
<div class="card-body text-center">
|
|
305
|
+
<h6 class="text-muted mb-1">Total Deducciones</h6>
|
|
306
|
+
<h4 class="text-danger mb-0">{{ planilla.moneda.simbolo if planilla.moneda else '' }} {{ "{:,.2f}".format(nomina.total_deducciones|float) }}</h4>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
<div class="col-md-4">
|
|
311
|
+
<div class="card bg-light">
|
|
312
|
+
<div class="card-body text-center">
|
|
313
|
+
<h6 class="text-muted mb-1">Total Neto</h6>
|
|
314
|
+
<h4 class="text-success mb-0">{{ planilla.moneda.simbolo if planilla.moneda else '' }} {{ "{:,.2f}".format(nomina.total_neto|float) }}</h4>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<div class="card">
|
|
323
|
+
<div class="card-header">
|
|
324
|
+
<h5 class="mb-0">Detalle por Empleado</h5>
|
|
325
|
+
</div>
|
|
326
|
+
<div class="card-body">
|
|
327
|
+
<table class="table table-striped table-hover">
|
|
328
|
+
<thead>
|
|
329
|
+
<tr>
|
|
330
|
+
<th>Empleado</th>
|
|
331
|
+
<th>Cargo</th>
|
|
332
|
+
<th class="text-right">Salario Base</th>
|
|
333
|
+
<th class="text-right">Ingresos</th>
|
|
334
|
+
<th class="text-right">Deducciones</th>
|
|
335
|
+
<th class="text-right">Neto</th>
|
|
336
|
+
<th>T/C</th>
|
|
337
|
+
<th>Acciones</th>
|
|
338
|
+
</tr>
|
|
339
|
+
</thead>
|
|
340
|
+
<tbody>
|
|
341
|
+
{% for ne in nomina_empleados %}
|
|
342
|
+
<tr>
|
|
343
|
+
<td>
|
|
344
|
+
<strong>{{ ne.empleado.primer_nombre }} {{ ne.empleado.primer_apellido }}</strong>
|
|
345
|
+
{% if ne.empleado.segundo_apellido %}{{ ne.empleado.segundo_apellido }}{% endif %}
|
|
346
|
+
</td>
|
|
347
|
+
<td>{{ ne.cargo_snapshot or ne.empleado.cargo or 'N/A' }}</td>
|
|
348
|
+
<td class="text-right">{{ "{:,.2f}".format(ne.sueldo_base_historico|float) }}</td>
|
|
349
|
+
<td class="text-right text-primary">+{{ "{:,.2f}".format(ne.total_ingresos|float) }}</td>
|
|
350
|
+
<td class="text-right text-danger">-{{ "{:,.2f}".format(ne.total_deducciones|float) }}</td>
|
|
351
|
+
<td class="text-right"><strong>{{ "{:,.2f}".format(ne.salario_neto|float) }}</strong></td>
|
|
352
|
+
<td>{{ "{:.4f}".format(ne.tipo_cambio_aplicado|float) if ne.tipo_cambio_aplicado else '1.0' }}</td>
|
|
353
|
+
<td>
|
|
354
|
+
<a href="{{ url_for('planilla.ver_nomina_empleado', planilla_id=planilla.id, nomina_id=nomina.id, nomina_empleado_id=ne.id) }}"
|
|
355
|
+
class="btn btn-sm btn-outline-primary" title="Ver Detalle">
|
|
356
|
+
<i class="bi bi-eye"></i>
|
|
357
|
+
</a>
|
|
358
|
+
</td>
|
|
359
|
+
</tr>
|
|
360
|
+
{% endfor %}
|
|
361
|
+
</tbody>
|
|
362
|
+
<tfoot>
|
|
363
|
+
<tr class="table-dark">
|
|
364
|
+
<th colspan="2">TOTALES</th>
|
|
365
|
+
<th class="text-right">-</th>
|
|
366
|
+
<th class="text-right">{{ "{:,.2f}".format(nomina.total_bruto|float) }}</th>
|
|
367
|
+
<th class="text-right">{{ "{:,.2f}".format(nomina.total_deducciones|float) }}</th>
|
|
368
|
+
<th class="text-right">{{ "{:,.2f}".format(nomina.total_neto|float) }}</th>
|
|
369
|
+
<th colspan="2"></th>
|
|
370
|
+
</tr>
|
|
371
|
+
</tfoot>
|
|
372
|
+
</table>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
{% if nomina.estado == 'calculando' %}
|
|
377
|
+
<script>
|
|
378
|
+
// Auto-refresh progress for nominas in "calculando" state
|
|
379
|
+
(function() {
|
|
380
|
+
const progressBar = document.getElementById('progress-bar');
|
|
381
|
+
const progressText = document.getElementById('progress-text');
|
|
382
|
+
const totalEmpleados = document.getElementById('total-empleados');
|
|
383
|
+
const empleadosProcesados = document.getElementById('empleados-procesados');
|
|
384
|
+
const empleadosConError = document.getElementById('empleados-con-error');
|
|
385
|
+
const empleadoActual = document.getElementById('empleado-actual');
|
|
386
|
+
const activityLog = document.getElementById('activity-log');
|
|
387
|
+
let autoScroll = true;
|
|
388
|
+
|
|
389
|
+
// Function to scroll log to bottom
|
|
390
|
+
window.scrollLogToBottom = function() {
|
|
391
|
+
activityLog.scrollTop = activityLog.scrollHeight;
|
|
392
|
+
autoScroll = true;
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// Detect manual scroll
|
|
396
|
+
activityLog.addEventListener('scroll', function() {
|
|
397
|
+
const isAtBottom = activityLog.scrollHeight - activityLog.scrollTop <= activityLog.clientHeight + 50;
|
|
398
|
+
autoScroll = isAtBottom;
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
function updateProgress() {
|
|
402
|
+
fetch('{{ url_for("planilla.progreso_nomina", planilla_id=planilla.id, nomina_id=nomina.id) }}')
|
|
403
|
+
.then(response => response.json())
|
|
404
|
+
.then(data => {
|
|
405
|
+
// Update progress bar
|
|
406
|
+
const progreso = data.progreso_porcentaje || 0;
|
|
407
|
+
progressBar.style.width = progreso + '%';
|
|
408
|
+
progressBar.setAttribute('aria-valuenow', progreso);
|
|
409
|
+
progressText.textContent = progreso + '% (' + data.empleados_procesados + '/' + data.total_empleados + ')';
|
|
410
|
+
|
|
411
|
+
// Update counters
|
|
412
|
+
totalEmpleados.textContent = data.total_empleados || 0;
|
|
413
|
+
empleadosProcesados.textContent = data.empleados_procesados || 0;
|
|
414
|
+
empleadosConError.textContent = data.empleados_con_error || 0;
|
|
415
|
+
|
|
416
|
+
// Update current employee
|
|
417
|
+
if (data.empleado_actual) {
|
|
418
|
+
empleadoActual.textContent = data.empleado_actual;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Update activity log
|
|
422
|
+
if (data.log_procesamiento && data.log_procesamiento.length > 0) {
|
|
423
|
+
let logHtml = '';
|
|
424
|
+
data.log_procesamiento.forEach(entry => {
|
|
425
|
+
let statusClass = 'text-muted';
|
|
426
|
+
if (entry.status === 'success') {
|
|
427
|
+
statusClass = 'text-success';
|
|
428
|
+
} else if (entry.status === 'error') {
|
|
429
|
+
statusClass = 'text-danger';
|
|
430
|
+
}
|
|
431
|
+
logHtml += '<div class="log-entry ' + statusClass + '">' +
|
|
432
|
+
escapeHtml(entry.message) + '</div>';
|
|
433
|
+
});
|
|
434
|
+
activityLog.innerHTML = logHtml;
|
|
435
|
+
|
|
436
|
+
// Auto-scroll to bottom if user hasn't manually scrolled
|
|
437
|
+
if (autoScroll) {
|
|
438
|
+
activityLog.scrollTop = activityLog.scrollHeight;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// If calculation is complete, reload page to show results
|
|
443
|
+
if (data.estado !== 'calculando') {
|
|
444
|
+
setTimeout(() => {
|
|
445
|
+
window.location.reload();
|
|
446
|
+
}, 1000);
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
.catch(error => {
|
|
450
|
+
console.error('Error fetching progress:', error);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Helper function to escape HTML
|
|
455
|
+
function escapeHtml(text) {
|
|
456
|
+
const div = document.createElement('div');
|
|
457
|
+
div.textContent = text;
|
|
458
|
+
return div.innerHTML;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Update immediately
|
|
462
|
+
updateProgress();
|
|
463
|
+
|
|
464
|
+
// Poll every 3 seconds
|
|
465
|
+
const intervalId = setInterval(updateProgress, 3000);
|
|
466
|
+
|
|
467
|
+
// Clear interval when page is unloaded
|
|
468
|
+
window.addEventListener('beforeunload', () => {
|
|
469
|
+
clearInterval(intervalId);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Initial scroll to bottom
|
|
473
|
+
scrollLogToBottom();
|
|
474
|
+
})();
|
|
475
|
+
</script>
|
|
476
|
+
{% endif %}
|
|
477
|
+
{% endblock %}
|