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,227 @@
|
|
|
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
|
+
"""Routes for managing novedades (novelties)."""
|
|
15
|
+
|
|
16
|
+
from flask import flash, redirect, render_template, request, url_for
|
|
17
|
+
from flask_login import current_user
|
|
18
|
+
|
|
19
|
+
from coati_payroll.model import db, Planilla, Nomina, NominaNovedad
|
|
20
|
+
from coati_payroll.forms import NominaNovedadForm
|
|
21
|
+
from coati_payroll.i18n import _
|
|
22
|
+
from coati_payroll.rbac import require_read_access, require_write_access
|
|
23
|
+
from coati_payroll.vistas.planilla import planilla_bp
|
|
24
|
+
from coati_payroll.vistas.planilla.helpers import populate_novedad_form_choices
|
|
25
|
+
from coati_payroll.vistas.planilla.services import NovedadService
|
|
26
|
+
|
|
27
|
+
# Constants
|
|
28
|
+
ROUTE_LISTAR_NOVEDADES = "planilla.listar_novedades"
|
|
29
|
+
ROUTE_LISTAR_NOMINAS = "planilla.listar_nominas"
|
|
30
|
+
ERROR_NOMINA_NO_PERTENECE = "La nómina no pertenece a esta planilla."
|
|
31
|
+
TEMPLATE_NOVEDAD_FORM = "modules/planilla/novedades/form.html"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@planilla_bp.route("/<planilla_id>/nomina/<nomina_id>/novedades")
|
|
35
|
+
@require_read_access()
|
|
36
|
+
def listar_novedades(planilla_id: str, nomina_id: str):
|
|
37
|
+
"""List all novedades (novelties) for a specific nomina."""
|
|
38
|
+
planilla = db.get_or_404(Planilla, planilla_id)
|
|
39
|
+
nomina = db.get_or_404(Nomina, nomina_id)
|
|
40
|
+
|
|
41
|
+
if nomina.planilla_id != planilla_id:
|
|
42
|
+
flash(_(ERROR_NOMINA_NO_PERTENECE), "error")
|
|
43
|
+
return redirect(url_for(ROUTE_LISTAR_NOMINAS, planilla_id=planilla_id))
|
|
44
|
+
|
|
45
|
+
novedades = NovedadService.listar_novedades(planilla, nomina)
|
|
46
|
+
|
|
47
|
+
return render_template(
|
|
48
|
+
"modules/planilla/novedades/index.html",
|
|
49
|
+
planilla=planilla,
|
|
50
|
+
nomina=nomina,
|
|
51
|
+
novedades=novedades,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@planilla_bp.route("/<planilla_id>/nomina/<nomina_id>/novedades/new", methods=["GET", "POST"])
|
|
56
|
+
@require_write_access()
|
|
57
|
+
def nueva_novedad(planilla_id: str, nomina_id: str):
|
|
58
|
+
"""Add a new novedad to a nomina."""
|
|
59
|
+
planilla = db.get_or_404(Planilla, planilla_id)
|
|
60
|
+
nomina = db.get_or_404(Nomina, nomina_id)
|
|
61
|
+
|
|
62
|
+
if nomina.planilla_id != planilla_id:
|
|
63
|
+
flash(_(ERROR_NOMINA_NO_PERTENECE), "error")
|
|
64
|
+
return redirect(url_for(ROUTE_LISTAR_NOMINAS, planilla_id=planilla_id))
|
|
65
|
+
|
|
66
|
+
if nomina.estado == "aplicado":
|
|
67
|
+
flash(_("No se pueden agregar novedades a una nómina aplicada."), "error")
|
|
68
|
+
return redirect(
|
|
69
|
+
url_for(
|
|
70
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
71
|
+
planilla_id=planilla_id,
|
|
72
|
+
nomina_id=nomina_id,
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
form = NominaNovedadForm()
|
|
77
|
+
populate_novedad_form_choices(form, nomina_id)
|
|
78
|
+
|
|
79
|
+
if form.validate_on_submit():
|
|
80
|
+
# Validate that fecha_novedad falls within the nomina period
|
|
81
|
+
is_valid, error_message = NovedadService.validar_fecha_novedad(form.fecha_novedad.data, nomina)
|
|
82
|
+
if not is_valid:
|
|
83
|
+
flash(error_message, "error")
|
|
84
|
+
return render_template(
|
|
85
|
+
TEMPLATE_NOVEDAD_FORM,
|
|
86
|
+
form=form,
|
|
87
|
+
planilla=planilla,
|
|
88
|
+
nomina=nomina,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
NovedadService.crear_novedad(nomina, form, current_user.usuario)
|
|
92
|
+
flash(_("Novedad agregada exitosamente."), "success")
|
|
93
|
+
return redirect(
|
|
94
|
+
url_for(
|
|
95
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
96
|
+
planilla_id=planilla_id,
|
|
97
|
+
nomina_id=nomina_id,
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return render_template(
|
|
102
|
+
TEMPLATE_NOVEDAD_FORM,
|
|
103
|
+
planilla=planilla,
|
|
104
|
+
nomina=nomina,
|
|
105
|
+
form=form,
|
|
106
|
+
is_edit=False,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@planilla_bp.route(
|
|
111
|
+
"/<planilla_id>/nomina/<nomina_id>/novedades/<novedad_id>/edit",
|
|
112
|
+
methods=["GET", "POST"],
|
|
113
|
+
)
|
|
114
|
+
@require_write_access()
|
|
115
|
+
def editar_novedad(planilla_id: str, nomina_id: str, novedad_id: str):
|
|
116
|
+
"""Edit an existing novedad."""
|
|
117
|
+
planilla = db.get_or_404(Planilla, planilla_id)
|
|
118
|
+
nomina = db.get_or_404(Nomina, nomina_id)
|
|
119
|
+
novedad = db.get_or_404(NominaNovedad, novedad_id)
|
|
120
|
+
|
|
121
|
+
if nomina.planilla_id != planilla_id:
|
|
122
|
+
flash(_(ERROR_NOMINA_NO_PERTENECE), "error")
|
|
123
|
+
return redirect(url_for(ROUTE_LISTAR_NOMINAS, planilla_id=planilla_id))
|
|
124
|
+
|
|
125
|
+
if novedad.nomina_id != nomina_id:
|
|
126
|
+
flash(_("La novedad no pertenece a esta nómina."), "error")
|
|
127
|
+
return redirect(
|
|
128
|
+
url_for(
|
|
129
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
130
|
+
planilla_id=planilla_id,
|
|
131
|
+
nomina_id=nomina_id,
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if nomina.estado == "aplicado":
|
|
136
|
+
flash(_("No se pueden editar novedades de una nómina aplicada."), "error")
|
|
137
|
+
return redirect(
|
|
138
|
+
url_for(
|
|
139
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
140
|
+
planilla_id=planilla_id,
|
|
141
|
+
nomina_id=nomina_id,
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
form = NominaNovedadForm(obj=novedad)
|
|
146
|
+
populate_novedad_form_choices(form, nomina_id)
|
|
147
|
+
|
|
148
|
+
# Set tipo_concepto based on existing data
|
|
149
|
+
if request.method == "GET":
|
|
150
|
+
if novedad.percepcion_id:
|
|
151
|
+
form.tipo_concepto.data = "percepcion"
|
|
152
|
+
form.percepcion_id.data = novedad.percepcion_id
|
|
153
|
+
elif novedad.deduccion_id:
|
|
154
|
+
form.tipo_concepto.data = "deduccion"
|
|
155
|
+
form.deduccion_id.data = novedad.deduccion_id
|
|
156
|
+
|
|
157
|
+
if form.validate_on_submit():
|
|
158
|
+
# Validate that fecha_novedad falls within the nomina period
|
|
159
|
+
is_valid, error_message = NovedadService.validar_fecha_novedad(form.fecha_novedad.data, nomina)
|
|
160
|
+
if not is_valid:
|
|
161
|
+
flash(error_message, "error")
|
|
162
|
+
return render_template(
|
|
163
|
+
TEMPLATE_NOVEDAD_FORM,
|
|
164
|
+
form=form,
|
|
165
|
+
planilla=planilla,
|
|
166
|
+
nomina=nomina,
|
|
167
|
+
novedad=novedad,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
NovedadService.actualizar_novedad(novedad, form, current_user.usuario)
|
|
171
|
+
flash(_("Novedad actualizada exitosamente."), "success")
|
|
172
|
+
return redirect(
|
|
173
|
+
url_for(
|
|
174
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
175
|
+
planilla_id=planilla_id,
|
|
176
|
+
nomina_id=nomina_id,
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return render_template(
|
|
181
|
+
TEMPLATE_NOVEDAD_FORM,
|
|
182
|
+
planilla=planilla,
|
|
183
|
+
nomina=nomina,
|
|
184
|
+
form=form,
|
|
185
|
+
novedad=novedad,
|
|
186
|
+
is_edit=True,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@planilla_bp.route(
|
|
191
|
+
"/<planilla_id>/nomina/<nomina_id>/novedades/<novedad_id>/delete",
|
|
192
|
+
methods=["POST"],
|
|
193
|
+
)
|
|
194
|
+
@require_write_access()
|
|
195
|
+
def eliminar_novedad(planilla_id: str, nomina_id: str, novedad_id: str):
|
|
196
|
+
"""Delete a novedad from a nomina."""
|
|
197
|
+
nomina = db.get_or_404(Nomina, nomina_id)
|
|
198
|
+
novedad = db.get_or_404(NominaNovedad, novedad_id)
|
|
199
|
+
|
|
200
|
+
if nomina.planilla_id != planilla_id:
|
|
201
|
+
flash(_(ERROR_NOMINA_NO_PERTENECE), "error")
|
|
202
|
+
return redirect(url_for(ROUTE_LISTAR_NOMINAS, planilla_id=planilla_id))
|
|
203
|
+
|
|
204
|
+
if novedad.nomina_id != nomina_id:
|
|
205
|
+
flash(_("La novedad no pertenece a esta nómina."), "error")
|
|
206
|
+
return redirect(
|
|
207
|
+
url_for(
|
|
208
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
209
|
+
planilla_id=planilla_id,
|
|
210
|
+
nomina_id=nomina_id,
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if nomina.estado == "aplicado":
|
|
215
|
+
flash(_("No se pueden eliminar novedades de una nómina aplicada."), "error")
|
|
216
|
+
return redirect(
|
|
217
|
+
url_for(
|
|
218
|
+
ROUTE_LISTAR_NOVEDADES,
|
|
219
|
+
planilla_id=planilla_id,
|
|
220
|
+
nomina_id=nomina_id,
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
db.session.delete(novedad)
|
|
225
|
+
db.session.commit()
|
|
226
|
+
flash(_("Novedad eliminada exitosamente."), "success")
|
|
227
|
+
return redirect(url_for(ROUTE_LISTAR_NOVEDADES, planilla_id=planilla_id, nomina_id=nomina_id))
|
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
"""Main routes for planilla management."""
|
|
15
|
+
|
|
16
|
+
from flask import flash, redirect, render_template, url_for
|
|
17
|
+
from flask_login import current_user
|
|
18
|
+
|
|
19
|
+
from coati_payroll.model import db, Planilla
|
|
20
|
+
from coati_payroll.forms import PlanillaForm
|
|
21
|
+
from coati_payroll.i18n import _
|
|
22
|
+
from coati_payroll.rbac import require_read_access, require_write_access
|
|
23
|
+
from coati_payroll.vistas.planilla.helpers import populate_form_choices, get_planilla_component_counts
|
|
24
|
+
from coati_payroll.vistas.planilla.services import PlanillaService
|
|
25
|
+
|
|
26
|
+
# Import blueprint from __init__.py
|
|
27
|
+
from coati_payroll.vistas.planilla import planilla_bp
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@planilla_bp.route("/")
|
|
31
|
+
@require_read_access()
|
|
32
|
+
def index():
|
|
33
|
+
"""List all planillas."""
|
|
34
|
+
planillas = db.session.execute(db.select(Planilla).order_by(Planilla.nombre)).scalars().all()
|
|
35
|
+
return render_template("modules/planilla/index.html", planillas=planillas)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@planilla_bp.route("/new", methods=["GET", "POST"])
|
|
39
|
+
@require_write_access()
|
|
40
|
+
def new():
|
|
41
|
+
"""Create a new planilla. Admin and HR can create planillas."""
|
|
42
|
+
form = PlanillaForm()
|
|
43
|
+
populate_form_choices(form)
|
|
44
|
+
|
|
45
|
+
if form.validate_on_submit():
|
|
46
|
+
planilla = Planilla(
|
|
47
|
+
nombre=form.nombre.data,
|
|
48
|
+
descripcion=form.descripcion.data,
|
|
49
|
+
tipo_planilla_id=form.tipo_planilla_id.data,
|
|
50
|
+
moneda_id=form.moneda_id.data,
|
|
51
|
+
empresa_id=form.empresa_id.data or None,
|
|
52
|
+
periodo_fiscal_inicio=form.periodo_fiscal_inicio.data,
|
|
53
|
+
periodo_fiscal_fin=form.periodo_fiscal_fin.data,
|
|
54
|
+
prioridad_prestamos=form.prioridad_prestamos.data or 250,
|
|
55
|
+
prioridad_adelantos=form.prioridad_adelantos.data or 251,
|
|
56
|
+
aplicar_prestamos_automatico=form.aplicar_prestamos_automatico.data,
|
|
57
|
+
aplicar_adelantos_automatico=form.aplicar_adelantos_automatico.data,
|
|
58
|
+
codigo_cuenta_debe_salario=form.codigo_cuenta_debe_salario.data,
|
|
59
|
+
descripcion_cuenta_debe_salario=form.descripcion_cuenta_debe_salario.data,
|
|
60
|
+
codigo_cuenta_haber_salario=form.codigo_cuenta_haber_salario.data,
|
|
61
|
+
descripcion_cuenta_haber_salario=form.descripcion_cuenta_haber_salario.data,
|
|
62
|
+
activo=form.activo.data,
|
|
63
|
+
creado_por=current_user.usuario,
|
|
64
|
+
)
|
|
65
|
+
db.session.add(planilla)
|
|
66
|
+
db.session.commit()
|
|
67
|
+
flash(_("Planilla creada exitosamente."), "success")
|
|
68
|
+
return redirect(url_for("planilla.edit", planilla_id=planilla.id))
|
|
69
|
+
|
|
70
|
+
return render_template("modules/planilla/form.html", form=form, is_edit=False)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@planilla_bp.route("/<planilla_id>/edit", methods=["GET", "POST"])
|
|
74
|
+
@require_write_access()
|
|
75
|
+
def edit(planilla_id: str):
|
|
76
|
+
"""Edit basic planilla configuration."""
|
|
77
|
+
planilla = db.get_or_404(Planilla, planilla_id)
|
|
78
|
+
form = PlanillaForm(obj=planilla)
|
|
79
|
+
populate_form_choices(form)
|
|
80
|
+
|
|
81
|
+
if form.validate_on_submit():
|
|
82
|
+
planilla.nombre = form.nombre.data
|
|
83
|
+
planilla.descripcion = form.descripcion.data
|
|
84
|
+
planilla.tipo_planilla_id = form.tipo_planilla_id.data
|
|
85
|
+
planilla.moneda_id = form.moneda_id.data
|
|
86
|
+
planilla.empresa_id = form.empresa_id.data or None
|
|
87
|
+
planilla.periodo_fiscal_inicio = form.periodo_fiscal_inicio.data
|
|
88
|
+
planilla.periodo_fiscal_fin = form.periodo_fiscal_fin.data
|
|
89
|
+
planilla.prioridad_prestamos = form.prioridad_prestamos.data or 250
|
|
90
|
+
planilla.prioridad_adelantos = form.prioridad_adelantos.data or 251
|
|
91
|
+
planilla.aplicar_prestamos_automatico = form.aplicar_prestamos_automatico.data
|
|
92
|
+
planilla.aplicar_adelantos_automatico = form.aplicar_adelantos_automatico.data
|
|
93
|
+
planilla.codigo_cuenta_debe_salario = form.codigo_cuenta_debe_salario.data
|
|
94
|
+
planilla.descripcion_cuenta_debe_salario = form.descripcion_cuenta_debe_salario.data
|
|
95
|
+
planilla.codigo_cuenta_haber_salario = form.codigo_cuenta_haber_salario.data
|
|
96
|
+
planilla.descripcion_cuenta_haber_salario = form.descripcion_cuenta_haber_salario.data
|
|
97
|
+
planilla.activo = form.activo.data
|
|
98
|
+
planilla.modificado_por = current_user.usuario
|
|
99
|
+
db.session.commit()
|
|
100
|
+
flash(_("Planilla actualizada exitosamente."), "success")
|
|
101
|
+
return redirect(url_for("planilla.config", planilla_id=planilla.id))
|
|
102
|
+
|
|
103
|
+
# Get association counts for the summary
|
|
104
|
+
counts = get_planilla_component_counts(planilla_id)
|
|
105
|
+
|
|
106
|
+
return render_template(
|
|
107
|
+
"modules/planilla/form.html",
|
|
108
|
+
form=form,
|
|
109
|
+
planilla=planilla,
|
|
110
|
+
is_edit=True,
|
|
111
|
+
**counts,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@planilla_bp.route("/<planilla_id>/config")
|
|
116
|
+
@require_read_access()
|
|
117
|
+
def config(planilla_id: str):
|
|
118
|
+
"""Configuration overview page for a planilla."""
|
|
119
|
+
planilla = db.get_or_404(Planilla, planilla_id)
|
|
120
|
+
|
|
121
|
+
# Get association counts for the summary
|
|
122
|
+
counts = get_planilla_component_counts(planilla_id)
|
|
123
|
+
|
|
124
|
+
return render_template(
|
|
125
|
+
"modules/planilla/config.html",
|
|
126
|
+
planilla=planilla,
|
|
127
|
+
**counts,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@planilla_bp.route("/<planilla_id>/delete", methods=["POST"])
|
|
132
|
+
@require_write_access()
|
|
133
|
+
def delete(planilla_id: str):
|
|
134
|
+
"""Delete a planilla."""
|
|
135
|
+
planilla = db.get_or_404(Planilla, planilla_id)
|
|
136
|
+
|
|
137
|
+
can_delete, error_message = PlanillaService.can_delete(planilla)
|
|
138
|
+
if not can_delete:
|
|
139
|
+
flash(_(error_message), "error")
|
|
140
|
+
return redirect(url_for("planilla.index"))
|
|
141
|
+
|
|
142
|
+
db.session.delete(planilla)
|
|
143
|
+
db.session.commit()
|
|
144
|
+
flash(_("Planilla eliminada exitosamente."), "success")
|
|
145
|
+
return redirect(url_for("planilla.index"))
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
"""Services for planilla business logic."""
|
|
15
|
+
|
|
16
|
+
from coati_payroll.vistas.planilla.services.planilla_service import PlanillaService
|
|
17
|
+
from coati_payroll.vistas.planilla.services.nomina_service import NominaService
|
|
18
|
+
from coati_payroll.vistas.planilla.services.export_service import ExportService
|
|
19
|
+
from coati_payroll.vistas.planilla.services.novedad_service import NovedadService
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"PlanillaService",
|
|
23
|
+
"NominaService",
|
|
24
|
+
"ExportService",
|
|
25
|
+
"NovedadService",
|
|
26
|
+
]
|