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,272 @@
|
|
|
1
|
+
# Copyright 2025 BMO Soluciones, S.A.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
"""Prestacion (Benefits) module views.
|
|
15
|
+
|
|
16
|
+
This module provides views for managing benefits including a dashboard
|
|
17
|
+
and bulk loading capabilities similar to the vacation module.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from decimal import Decimal
|
|
23
|
+
|
|
24
|
+
from flask import Blueprint, flash, redirect, render_template, request, url_for
|
|
25
|
+
from flask_login import current_user, login_required
|
|
26
|
+
from sqlalchemy import func
|
|
27
|
+
|
|
28
|
+
from coati_payroll.enums import TipoUsuario
|
|
29
|
+
from coati_payroll.i18n import _
|
|
30
|
+
from coati_payroll.model import (
|
|
31
|
+
db,
|
|
32
|
+
Prestacion,
|
|
33
|
+
PrestacionAcumulada,
|
|
34
|
+
CargaInicialPrestacion,
|
|
35
|
+
Empleado,
|
|
36
|
+
Moneda,
|
|
37
|
+
)
|
|
38
|
+
from coati_payroll.rbac import require_role
|
|
39
|
+
|
|
40
|
+
# Constants
|
|
41
|
+
MAX_DISPLAYED_ERRORS = 10 # Maximum number of errors to display in bulk upload results
|
|
42
|
+
|
|
43
|
+
prestacion_management_bp = Blueprint("prestacion_management", __name__, url_prefix="/prestacion-management")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ============================================================================
|
|
47
|
+
# Prestacion Dashboard
|
|
48
|
+
# ============================================================================
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@prestacion_management_bp.route("/")
|
|
52
|
+
@login_required
|
|
53
|
+
def dashboard():
|
|
54
|
+
"""Prestacion management dashboard."""
|
|
55
|
+
# Statistics
|
|
56
|
+
total_prestaciones = (
|
|
57
|
+
db.session.execute(db.select(func.count(Prestacion.id)).filter(Prestacion.activo.is_(True))).scalar() or 0
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Count employees with benefit balances
|
|
61
|
+
total_accounts = (
|
|
62
|
+
db.session.execute(db.select(func.count(func.distinct(PrestacionAcumulada.empleado_id)))).scalar() or 0
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Count pending initial loads
|
|
66
|
+
pending_loads = (
|
|
67
|
+
db.session.execute(
|
|
68
|
+
db.select(func.count(CargaInicialPrestacion.id)).filter(CargaInicialPrestacion.estado == "borrador")
|
|
69
|
+
).scalar()
|
|
70
|
+
or 0
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Recent activity - latest transactions
|
|
74
|
+
recent_transactions = (
|
|
75
|
+
db.session.execute(
|
|
76
|
+
db.select(PrestacionAcumulada)
|
|
77
|
+
.join(PrestacionAcumulada.empleado)
|
|
78
|
+
.order_by(PrestacionAcumulada.fecha_transaccion.desc())
|
|
79
|
+
.limit(10)
|
|
80
|
+
)
|
|
81
|
+
.scalars()
|
|
82
|
+
.all()
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return render_template(
|
|
86
|
+
"modules/prestacion_management/dashboard.html",
|
|
87
|
+
total_prestaciones=total_prestaciones,
|
|
88
|
+
total_accounts=total_accounts,
|
|
89
|
+
pending_loads=pending_loads,
|
|
90
|
+
recent_transactions=recent_transactions,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ============================================================================
|
|
95
|
+
# Initial Balance Loading (for System Implementation)
|
|
96
|
+
# ============================================================================
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@prestacion_management_bp.route("/initial-balance/bulk", methods=["GET", "POST"])
|
|
100
|
+
@require_role(TipoUsuario.ADMIN)
|
|
101
|
+
def initial_balance_bulk():
|
|
102
|
+
"""Bulk load initial prestacion balances from Excel.
|
|
103
|
+
|
|
104
|
+
Used during system implementation for companies with many employees.
|
|
105
|
+
Allows uploading an Excel file with initial prestacion balances for multiple
|
|
106
|
+
employees at once.
|
|
107
|
+
|
|
108
|
+
Expected Excel format (without headers, data starts on row 1):
|
|
109
|
+
- Column A: Código de Empleado
|
|
110
|
+
- Column B: Código de Prestación
|
|
111
|
+
- Column C: Año de Corte
|
|
112
|
+
- Column D: Mes de Corte
|
|
113
|
+
- Column E: Código de Moneda
|
|
114
|
+
- Column F: Saldo Acumulado
|
|
115
|
+
- Column G: Tipo de Cambio (opcional, default 1.0)
|
|
116
|
+
- Column H: Observaciones (opcional)
|
|
117
|
+
"""
|
|
118
|
+
if request.method == "POST":
|
|
119
|
+
# Check if file was uploaded
|
|
120
|
+
if "file" not in request.files:
|
|
121
|
+
flash(_("No se seleccionó ningún archivo."), "warning")
|
|
122
|
+
return redirect(url_for("prestacion_management.initial_balance_bulk"))
|
|
123
|
+
|
|
124
|
+
file = request.files["file"]
|
|
125
|
+
|
|
126
|
+
if file.filename == "":
|
|
127
|
+
flash(_("No se seleccionó ningún archivo."), "warning")
|
|
128
|
+
return redirect(url_for("prestacion_management.initial_balance_bulk"))
|
|
129
|
+
|
|
130
|
+
if not file.filename.endswith((".xlsx", ".xls")):
|
|
131
|
+
flash(_("Por favor, suba un archivo Excel (.xlsx o .xls)."), "warning")
|
|
132
|
+
return redirect(url_for("prestacion_management.initial_balance_bulk"))
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
import openpyxl
|
|
136
|
+
|
|
137
|
+
# Load Excel file
|
|
138
|
+
workbook = openpyxl.load_workbook(file, data_only=True)
|
|
139
|
+
sheet = workbook.active
|
|
140
|
+
|
|
141
|
+
success_count = 0
|
|
142
|
+
error_count = 0
|
|
143
|
+
errors = []
|
|
144
|
+
|
|
145
|
+
# Process each row (data starts at row 1, no headers expected)
|
|
146
|
+
for row_num, row in enumerate(sheet.iter_rows(min_row=1, values_only=True), start=1):
|
|
147
|
+
codigo_empleado = row[0]
|
|
148
|
+
codigo_prestacion = row[1]
|
|
149
|
+
anio_corte = row[2]
|
|
150
|
+
mes_corte = row[3]
|
|
151
|
+
codigo_moneda = row[4]
|
|
152
|
+
saldo_acumulado = row[5]
|
|
153
|
+
tipo_cambio = row[6] if len(row) > 6 and row[6] is not None else Decimal("1.0")
|
|
154
|
+
observaciones = row[7] if len(row) > 7 else "Carga masiva de saldo inicial"
|
|
155
|
+
|
|
156
|
+
# Validate required fields
|
|
157
|
+
if not all(
|
|
158
|
+
[
|
|
159
|
+
codigo_empleado,
|
|
160
|
+
codigo_prestacion,
|
|
161
|
+
anio_corte,
|
|
162
|
+
mes_corte,
|
|
163
|
+
codigo_moneda,
|
|
164
|
+
saldo_acumulado is not None,
|
|
165
|
+
]
|
|
166
|
+
):
|
|
167
|
+
errors.append(f"Fila {row_num}: Faltan campos requeridos")
|
|
168
|
+
error_count += 1
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
# Find employee
|
|
172
|
+
empleado = db.session.execute(
|
|
173
|
+
db.select(Empleado).filter(Empleado.codigo_empleado == codigo_empleado, Empleado.activo.is_(True))
|
|
174
|
+
).scalar_one_or_none()
|
|
175
|
+
|
|
176
|
+
if not empleado:
|
|
177
|
+
errors.append(f"Fila {row_num}: Empleado {codigo_empleado} no encontrado")
|
|
178
|
+
error_count += 1
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
# Find prestacion
|
|
182
|
+
prestacion = db.session.execute(
|
|
183
|
+
db.select(Prestacion).filter(Prestacion.codigo == codigo_prestacion, Prestacion.activo.is_(True))
|
|
184
|
+
).scalar_one_or_none()
|
|
185
|
+
|
|
186
|
+
if not prestacion:
|
|
187
|
+
errors.append(f"Fila {row_num}: Prestación {codigo_prestacion} no encontrada")
|
|
188
|
+
error_count += 1
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
# Find moneda
|
|
192
|
+
moneda = db.session.execute(
|
|
193
|
+
db.select(Moneda).filter(Moneda.codigo == codigo_moneda, Moneda.activo.is_(True))
|
|
194
|
+
).scalar_one_or_none()
|
|
195
|
+
|
|
196
|
+
if not moneda:
|
|
197
|
+
errors.append(f"Fila {row_num}: Moneda {codigo_moneda} no encontrada")
|
|
198
|
+
error_count += 1
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
# Check for duplicate
|
|
202
|
+
existing = db.session.execute(
|
|
203
|
+
db.select(CargaInicialPrestacion).filter(
|
|
204
|
+
CargaInicialPrestacion.empleado_id == empleado.id,
|
|
205
|
+
CargaInicialPrestacion.prestacion_id == prestacion.id,
|
|
206
|
+
CargaInicialPrestacion.anio_corte == anio_corte,
|
|
207
|
+
CargaInicialPrestacion.mes_corte == mes_corte,
|
|
208
|
+
)
|
|
209
|
+
).scalar_one_or_none()
|
|
210
|
+
|
|
211
|
+
if existing:
|
|
212
|
+
errors.append(
|
|
213
|
+
f"Fila {row_num}: Duplicado {codigo_empleado}, {codigo_prestacion}, {mes_corte}/{anio_corte}"
|
|
214
|
+
)
|
|
215
|
+
error_count += 1
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
# Calculate saldo_convertido
|
|
220
|
+
saldo_convertido = Decimal(str(saldo_acumulado)) * Decimal(str(tipo_cambio))
|
|
221
|
+
|
|
222
|
+
# Create CargaInicialPrestacion
|
|
223
|
+
carga = CargaInicialPrestacion(
|
|
224
|
+
empleado_id=empleado.id,
|
|
225
|
+
prestacion_id=prestacion.id,
|
|
226
|
+
anio_corte=anio_corte,
|
|
227
|
+
mes_corte=mes_corte,
|
|
228
|
+
moneda_id=moneda.id,
|
|
229
|
+
saldo_acumulado=Decimal(str(saldo_acumulado)),
|
|
230
|
+
tipo_cambio=Decimal(str(tipo_cambio)),
|
|
231
|
+
saldo_convertido=saldo_convertido,
|
|
232
|
+
observaciones=str(observaciones) if observaciones else "Carga masiva de saldo inicial",
|
|
233
|
+
estado="borrador",
|
|
234
|
+
creado_por=current_user.usuario if current_user.is_authenticated else None,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
db.session.add(carga)
|
|
238
|
+
success_count += 1
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
errors.append(f"Fila {row_num}: Error al procesar {codigo_empleado}: {str(e)}")
|
|
242
|
+
error_count += 1
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
# Commit all changes
|
|
246
|
+
try:
|
|
247
|
+
db.session.commit()
|
|
248
|
+
flash(
|
|
249
|
+
_("Carga completada: {} registros exitosos en estado borrador, {} errores.").format(
|
|
250
|
+
success_count, error_count
|
|
251
|
+
),
|
|
252
|
+
"success" if error_count == 0 else "warning",
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
if errors:
|
|
256
|
+
error_details = "<br>".join(errors[:MAX_DISPLAYED_ERRORS])
|
|
257
|
+
if len(errors) > MAX_DISPLAYED_ERRORS:
|
|
258
|
+
error_details += f"<br>...y {len(errors) - MAX_DISPLAYED_ERRORS} errores más"
|
|
259
|
+
flash(error_details, "warning")
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
db.session.rollback()
|
|
263
|
+
flash(_("Error al guardar los cambios: {}").format(str(e)), "danger")
|
|
264
|
+
|
|
265
|
+
except ImportError:
|
|
266
|
+
flash(_("Error: La librería openpyxl no está instalada. Contacte al administrador."), "danger")
|
|
267
|
+
except Exception as e:
|
|
268
|
+
flash(_("Error al procesar el archivo Excel: {}").format(str(e)), "danger")
|
|
269
|
+
|
|
270
|
+
return redirect(url_for("prestacion_management.initial_balance_bulk"))
|
|
271
|
+
|
|
272
|
+
return render_template("modules/prestacion_management/initial_balance_bulk.html")
|