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,813 @@
|
|
|
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
|
+
"""Demo data loading for manual testing.
|
|
15
|
+
|
|
16
|
+
This module provides comprehensive sample data to facilitate manual testing
|
|
17
|
+
of the payroll system. Data is loaded when COATI_LOAD_DEMO_DATA environment
|
|
18
|
+
variable is set.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
# <-------------------------------------------------------------------------> #
|
|
24
|
+
# Standard library
|
|
25
|
+
# <-------------------------------------------------------------------------> #
|
|
26
|
+
from datetime import date, timedelta
|
|
27
|
+
from decimal import Decimal
|
|
28
|
+
from dateutil.relativedelta import relativedelta
|
|
29
|
+
|
|
30
|
+
# <-------------------------------------------------------------------------> #
|
|
31
|
+
# Third party libraries
|
|
32
|
+
# <-------------------------------------------------------------------------> #
|
|
33
|
+
|
|
34
|
+
# <-------------------------------------------------------------------------> #
|
|
35
|
+
# Local modules
|
|
36
|
+
# <-------------------------------------------------------------------------> #
|
|
37
|
+
from coati_payroll.model import (
|
|
38
|
+
db,
|
|
39
|
+
Empresa,
|
|
40
|
+
Empleado,
|
|
41
|
+
Moneda,
|
|
42
|
+
TipoPlanilla,
|
|
43
|
+
Planilla,
|
|
44
|
+
PlanillaEmpleado,
|
|
45
|
+
PlanillaIngreso,
|
|
46
|
+
PlanillaDeduccion,
|
|
47
|
+
PlanillaPrestacion,
|
|
48
|
+
Percepcion,
|
|
49
|
+
Deduccion,
|
|
50
|
+
Prestacion,
|
|
51
|
+
Nomina,
|
|
52
|
+
NominaNovedad,
|
|
53
|
+
)
|
|
54
|
+
from coati_payroll.log import log
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def load_demo_companies() -> tuple[Empresa, Empresa]:
|
|
58
|
+
"""Create two demo companies with complete information.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
tuple: Two Empresa objects (company1, company2)
|
|
62
|
+
"""
|
|
63
|
+
log.trace("Loading demo companies...")
|
|
64
|
+
|
|
65
|
+
empresa1 = db.session.execute(db.select(Empresa).filter_by(codigo="DEMO001")).scalar_one_or_none()
|
|
66
|
+
empresa2 = db.session.execute(db.select(Empresa).filter_by(codigo="DEMO002")).scalar_one_or_none()
|
|
67
|
+
|
|
68
|
+
if empresa1 is None:
|
|
69
|
+
empresa1 = Empresa()
|
|
70
|
+
empresa1.codigo = "DEMO001"
|
|
71
|
+
empresa1.razon_social = "Tecnología y Soluciones S.A."
|
|
72
|
+
empresa1.nombre_comercial = "TechSol"
|
|
73
|
+
empresa1.ruc = "J0310000123456"
|
|
74
|
+
empresa1.direccion = "Km 7.5 Carretera a Masaya, Managua"
|
|
75
|
+
empresa1.telefono = "+505 2255-1234"
|
|
76
|
+
empresa1.correo = "info@techsol-demo.com"
|
|
77
|
+
empresa1.sitio_web = "www.techsol-demo.com"
|
|
78
|
+
empresa1.representante_legal = "María Elena Gutiérrez"
|
|
79
|
+
empresa1.activo = True
|
|
80
|
+
db.session.add(empresa1)
|
|
81
|
+
log.trace("Created demo company: Tecnología y Soluciones S.A.")
|
|
82
|
+
|
|
83
|
+
if empresa2 is None:
|
|
84
|
+
empresa2 = Empresa()
|
|
85
|
+
empresa2.codigo = "DEMO002"
|
|
86
|
+
empresa2.razon_social = "Servicios Profesionales BMO Ltda."
|
|
87
|
+
empresa2.nombre_comercial = "BMO Services"
|
|
88
|
+
empresa2.ruc = "J0310000654321"
|
|
89
|
+
empresa2.direccion = "Plaza España, módulo 5, Managua"
|
|
90
|
+
empresa2.telefono = "+505 2277-5678"
|
|
91
|
+
empresa2.correo = "contacto@bmo-services-demo.com"
|
|
92
|
+
empresa2.sitio_web = "www.bmo-services-demo.com"
|
|
93
|
+
empresa2.representante_legal = "Carlos Antonio Ramírez"
|
|
94
|
+
empresa2.activo = True
|
|
95
|
+
db.session.add(empresa2)
|
|
96
|
+
log.trace("Created demo company: Servicios Profesionales BMO Ltda.")
|
|
97
|
+
|
|
98
|
+
db.session.commit()
|
|
99
|
+
return empresa1, empresa2
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def load_demo_employees(empresa1: Empresa, empresa2: Empresa) -> list[Empleado]:
|
|
103
|
+
"""Create diverse demo employees for testing.
|
|
104
|
+
|
|
105
|
+
Creates 15 employees with varied:
|
|
106
|
+
- Salaries (from minimum wage to executive level)
|
|
107
|
+
- Positions and departments
|
|
108
|
+
- Contract types and employment dates
|
|
109
|
+
- Personal information
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
empresa1: First demo company
|
|
113
|
+
empresa2: Second demo company
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
list: List of created Empleado objects
|
|
117
|
+
"""
|
|
118
|
+
log.trace("Loading demo employees...")
|
|
119
|
+
|
|
120
|
+
# Get currency (use NIO if available, otherwise first available)
|
|
121
|
+
moneda = db.session.execute(db.select(Moneda).filter_by(codigo="NIO")).scalar_one_or_none()
|
|
122
|
+
|
|
123
|
+
if moneda is None:
|
|
124
|
+
moneda = db.session.execute(db.select(Moneda)).scalars().first()
|
|
125
|
+
|
|
126
|
+
if moneda is None:
|
|
127
|
+
log.trace("No currency available for demo employees")
|
|
128
|
+
return []
|
|
129
|
+
|
|
130
|
+
empleados_data = [
|
|
131
|
+
# Company 1 - Tecnología y Soluciones
|
|
132
|
+
{
|
|
133
|
+
"codigo": "DEMO-EMP001",
|
|
134
|
+
"primer_nombre": "Juan",
|
|
135
|
+
"segundo_nombre": "Carlos",
|
|
136
|
+
"primer_apellido": "Pérez",
|
|
137
|
+
"segundo_apellido": "González",
|
|
138
|
+
"identificacion_personal": "001-150890-0001K",
|
|
139
|
+
"genero": "Masculino",
|
|
140
|
+
"nacionalidad": "Nicaragüense",
|
|
141
|
+
"fecha_nacimiento": date(1990, 8, 15),
|
|
142
|
+
"cargo": "Gerente de Tecnología",
|
|
143
|
+
"area": "Tecnología",
|
|
144
|
+
"salario_base": Decimal("35000.00"),
|
|
145
|
+
"tipo_contrato": "Indefinido",
|
|
146
|
+
"fecha_alta": date.today() - timedelta(days=730), # 2 years ago
|
|
147
|
+
"empresa": empresa1,
|
|
148
|
+
"correo": "jperez@techsol-demo.com",
|
|
149
|
+
"telefono": "+505 8888-1234",
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"codigo": "DEMO-EMP002",
|
|
153
|
+
"primer_nombre": "María",
|
|
154
|
+
"segundo_nombre": "Elena",
|
|
155
|
+
"primer_apellido": "Rodríguez",
|
|
156
|
+
"segundo_apellido": "Morales",
|
|
157
|
+
"identificacion_personal": "001-220685-0002M",
|
|
158
|
+
"genero": "Femenino",
|
|
159
|
+
"nacionalidad": "Nicaragüense",
|
|
160
|
+
"fecha_nacimiento": date(1985, 6, 22),
|
|
161
|
+
"cargo": "Desarrolladora Senior",
|
|
162
|
+
"area": "Tecnología",
|
|
163
|
+
"salario_base": Decimal("28000.00"),
|
|
164
|
+
"tipo_contrato": "Indefinido",
|
|
165
|
+
"fecha_alta": date.today() - timedelta(days=1095), # 3 years ago
|
|
166
|
+
"empresa": empresa1,
|
|
167
|
+
"correo": "mrodriguez@techsol-demo.com",
|
|
168
|
+
"telefono": "+505 8888-2345",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"codigo": "DEMO-EMP003",
|
|
172
|
+
"primer_nombre": "Carlos",
|
|
173
|
+
"segundo_nombre": "Alberto",
|
|
174
|
+
"primer_apellido": "Martínez",
|
|
175
|
+
"segundo_apellido": "López",
|
|
176
|
+
"identificacion_personal": "001-100992-0003N",
|
|
177
|
+
"genero": "Masculino",
|
|
178
|
+
"nacionalidad": "Nicaragüense",
|
|
179
|
+
"fecha_nacimiento": date(1992, 9, 10),
|
|
180
|
+
"cargo": "Desarrollador Junior",
|
|
181
|
+
"area": "Tecnología",
|
|
182
|
+
"salario_base": Decimal("15000.00"),
|
|
183
|
+
"tipo_contrato": "Temporal",
|
|
184
|
+
"fecha_alta": date.today() - timedelta(days=180), # 6 months ago
|
|
185
|
+
"empresa": empresa1,
|
|
186
|
+
"correo": "cmartinez@techsol-demo.com",
|
|
187
|
+
"telefono": "+505 8888-3456",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"codigo": "DEMO-EMP004",
|
|
191
|
+
"primer_nombre": "Ana",
|
|
192
|
+
"segundo_nombre": "Patricia",
|
|
193
|
+
"primer_apellido": "García",
|
|
194
|
+
"segundo_apellido": "Hernández",
|
|
195
|
+
"identificacion_personal": "001-050888-0004L",
|
|
196
|
+
"genero": "Femenino",
|
|
197
|
+
"nacionalidad": "Nicaragüense",
|
|
198
|
+
"fecha_nacimiento": date(1988, 8, 5),
|
|
199
|
+
"cargo": "Contador",
|
|
200
|
+
"area": "Finanzas",
|
|
201
|
+
"salario_base": Decimal("22000.00"),
|
|
202
|
+
"tipo_contrato": "Indefinido",
|
|
203
|
+
"fecha_alta": date.today() - timedelta(days=1460), # 4 years ago
|
|
204
|
+
"empresa": empresa1,
|
|
205
|
+
"correo": "agarcia@techsol-demo.com",
|
|
206
|
+
"telefono": "+505 8888-4567",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"codigo": "DEMO-EMP005",
|
|
210
|
+
"primer_nombre": "Roberto",
|
|
211
|
+
"segundo_nombre": "José",
|
|
212
|
+
"primer_apellido": "Flores",
|
|
213
|
+
"segundo_apellido": "Gutiérrez",
|
|
214
|
+
"identificacion_personal": "001-181195-0005P",
|
|
215
|
+
"genero": "Masculino",
|
|
216
|
+
"nacionalidad": "Nicaragüense",
|
|
217
|
+
"fecha_nacimiento": date(1995, 11, 18),
|
|
218
|
+
"cargo": "Asistente Administrativo",
|
|
219
|
+
"area": "Administración",
|
|
220
|
+
"salario_base": Decimal("12000.00"),
|
|
221
|
+
"tipo_contrato": "Indefinido",
|
|
222
|
+
"fecha_alta": date.today() - timedelta(days=365), # 1 year ago
|
|
223
|
+
"empresa": empresa1,
|
|
224
|
+
"correo": "rflores@techsol-demo.com",
|
|
225
|
+
"telefono": "+505 8888-5678",
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"codigo": "DEMO-EMP006",
|
|
229
|
+
"primer_nombre": "Laura",
|
|
230
|
+
"segundo_nombre": "Isabel",
|
|
231
|
+
"primer_apellido": "Ramírez",
|
|
232
|
+
"segundo_apellido": "Castro",
|
|
233
|
+
"identificacion_personal": "001-250687-0006R",
|
|
234
|
+
"genero": "Femenino",
|
|
235
|
+
"nacionalidad": "Nicaragüense",
|
|
236
|
+
"fecha_nacimiento": date(1987, 6, 25),
|
|
237
|
+
"cargo": "Jefa de Recursos Humanos",
|
|
238
|
+
"area": "Recursos Humanos",
|
|
239
|
+
"salario_base": Decimal("26000.00"),
|
|
240
|
+
"tipo_contrato": "Indefinido",
|
|
241
|
+
"fecha_alta": date.today() - timedelta(days=900), # ~2.5 years ago
|
|
242
|
+
"empresa": empresa1,
|
|
243
|
+
"correo": "lramirez@techsol-demo.com",
|
|
244
|
+
"telefono": "+505 8888-6789",
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"codigo": "DEMO-EMP007",
|
|
248
|
+
"primer_nombre": "Diego",
|
|
249
|
+
"segundo_nombre": "Andrés",
|
|
250
|
+
"primer_apellido": "Sánchez",
|
|
251
|
+
"segundo_apellido": "Vargas",
|
|
252
|
+
"identificacion_personal": "001-120993-0007T",
|
|
253
|
+
"genero": "Masculino",
|
|
254
|
+
"nacionalidad": "Nicaragüense",
|
|
255
|
+
"fecha_nacimiento": date(1993, 9, 12),
|
|
256
|
+
"cargo": "Analista de Soporte",
|
|
257
|
+
"area": "Tecnología",
|
|
258
|
+
"salario_base": Decimal("18000.00"),
|
|
259
|
+
"tipo_contrato": "Indefinido",
|
|
260
|
+
"fecha_alta": date.today() - timedelta(days=545), # ~1.5 years ago
|
|
261
|
+
"empresa": empresa1,
|
|
262
|
+
"correo": "dsanchez@techsol-demo.com",
|
|
263
|
+
"telefono": "+505 8888-7890",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"codigo": "DEMO-EMP008",
|
|
267
|
+
"primer_nombre": "Gabriela",
|
|
268
|
+
"segundo_nombre": "Sofía",
|
|
269
|
+
"primer_apellido": "Mendoza",
|
|
270
|
+
"segundo_apellido": "Ortiz",
|
|
271
|
+
"identificacion_personal": "001-080391-0008U",
|
|
272
|
+
"genero": "Femenino",
|
|
273
|
+
"nacionalidad": "Nicaragüense",
|
|
274
|
+
"fecha_nacimiento": date(1991, 3, 8),
|
|
275
|
+
"cargo": "Diseñadora UX/UI",
|
|
276
|
+
"area": "Tecnología",
|
|
277
|
+
"salario_base": Decimal("20000.00"),
|
|
278
|
+
"tipo_contrato": "Temporal",
|
|
279
|
+
"fecha_alta": date.today() - timedelta(days=270), # 9 months ago
|
|
280
|
+
"empresa": empresa1,
|
|
281
|
+
"correo": "gmendoza@techsol-demo.com",
|
|
282
|
+
"telefono": "+505 8888-8901",
|
|
283
|
+
},
|
|
284
|
+
# Company 2 - Servicios Profesionales BMO
|
|
285
|
+
{
|
|
286
|
+
"codigo": "DEMO-EMP009",
|
|
287
|
+
"primer_nombre": "Fernando",
|
|
288
|
+
"segundo_nombre": "Luis",
|
|
289
|
+
"primer_apellido": "Torres",
|
|
290
|
+
"segundo_apellido": "Ruiz",
|
|
291
|
+
"identificacion_personal": "001-301284-0009V",
|
|
292
|
+
"genero": "Masculino",
|
|
293
|
+
"nacionalidad": "Nicaragüense",
|
|
294
|
+
"fecha_nacimiento": date(1984, 12, 30),
|
|
295
|
+
"cargo": "Director General",
|
|
296
|
+
"area": "Dirección",
|
|
297
|
+
"salario_base": Decimal("50000.00"),
|
|
298
|
+
"tipo_contrato": "Indefinido",
|
|
299
|
+
"fecha_alta": date.today() - timedelta(days=1825), # 5 years ago
|
|
300
|
+
"empresa": empresa2,
|
|
301
|
+
"correo": "ftorres@bmo-services-demo.com",
|
|
302
|
+
"telefono": "+505 7777-1234",
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"codigo": "DEMO-EMP010",
|
|
306
|
+
"primer_nombre": "Patricia",
|
|
307
|
+
"segundo_nombre": "Mercedes",
|
|
308
|
+
"primer_apellido": "Jiménez",
|
|
309
|
+
"segundo_apellido": "Silva",
|
|
310
|
+
"identificacion_personal": "001-140689-0010W",
|
|
311
|
+
"genero": "Femenino",
|
|
312
|
+
"nacionalidad": "Nicaragüense",
|
|
313
|
+
"fecha_nacimiento": date(1989, 6, 14),
|
|
314
|
+
"cargo": "Consultora Senior",
|
|
315
|
+
"area": "Consultoría",
|
|
316
|
+
"salario_base": Decimal("32000.00"),
|
|
317
|
+
"tipo_contrato": "Indefinido",
|
|
318
|
+
"fecha_alta": date.today() - timedelta(days=1095), # 3 years ago
|
|
319
|
+
"empresa": empresa2,
|
|
320
|
+
"correo": "pjimenez@bmo-services-demo.com",
|
|
321
|
+
"telefono": "+505 7777-2345",
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
"codigo": "DEMO-EMP011",
|
|
325
|
+
"primer_nombre": "Miguel",
|
|
326
|
+
"segundo_nombre": "Ángel",
|
|
327
|
+
"primer_apellido": "Herrera",
|
|
328
|
+
"segundo_apellido": "Díaz",
|
|
329
|
+
"identificacion_personal": "001-221090-0011X",
|
|
330
|
+
"genero": "Masculino",
|
|
331
|
+
"nacionalidad": "Nicaragüense",
|
|
332
|
+
"fecha_nacimiento": date(1990, 10, 22),
|
|
333
|
+
"cargo": "Consultor",
|
|
334
|
+
"area": "Consultoría",
|
|
335
|
+
"salario_base": Decimal("24000.00"),
|
|
336
|
+
"tipo_contrato": "Indefinido",
|
|
337
|
+
"fecha_alta": date.today() - timedelta(days=730), # 2 years ago
|
|
338
|
+
"empresa": empresa2,
|
|
339
|
+
"correo": "mherrera@bmo-services-demo.com",
|
|
340
|
+
"telefono": "+505 7777-3456",
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"codigo": "DEMO-EMP012",
|
|
344
|
+
"primer_nombre": "Claudia",
|
|
345
|
+
"segundo_nombre": "Beatriz",
|
|
346
|
+
"primer_apellido": "Moreno",
|
|
347
|
+
"segundo_apellido": "Rivas",
|
|
348
|
+
"identificacion_personal": "001-190992-0012Y",
|
|
349
|
+
"genero": "Femenino",
|
|
350
|
+
"nacionalidad": "Nicaragüense",
|
|
351
|
+
"fecha_nacimiento": date(1992, 9, 19),
|
|
352
|
+
"cargo": "Asistente de Consultoría",
|
|
353
|
+
"area": "Consultoría",
|
|
354
|
+
"salario_base": Decimal("16000.00"),
|
|
355
|
+
"tipo_contrato": "Temporal",
|
|
356
|
+
"fecha_alta": date.today() - timedelta(days=365), # 1 year ago
|
|
357
|
+
"empresa": empresa2,
|
|
358
|
+
"correo": "cmoreno@bmo-services-demo.com",
|
|
359
|
+
"telefono": "+505 7777-4567",
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"codigo": "DEMO-EMP013",
|
|
363
|
+
"primer_nombre": "Sergio",
|
|
364
|
+
"segundo_nombre": "Rafael",
|
|
365
|
+
"primer_apellido": "Vega",
|
|
366
|
+
"segundo_apellido": "Campos",
|
|
367
|
+
"identificacion_personal": "001-051286-0013Z",
|
|
368
|
+
"genero": "Masculino",
|
|
369
|
+
"nacionalidad": "Nicaragüense",
|
|
370
|
+
"fecha_nacimiento": date(1986, 12, 5),
|
|
371
|
+
"cargo": "Contador General",
|
|
372
|
+
"area": "Finanzas",
|
|
373
|
+
"salario_base": Decimal("28000.00"),
|
|
374
|
+
"tipo_contrato": "Indefinido",
|
|
375
|
+
"fecha_alta": date.today() - timedelta(days=1460), # 4 years ago
|
|
376
|
+
"empresa": empresa2,
|
|
377
|
+
"correo": "svega@bmo-services-demo.com",
|
|
378
|
+
"telefono": "+505 7777-5678",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"codigo": "DEMO-EMP014",
|
|
382
|
+
"primer_nombre": "Lucía",
|
|
383
|
+
"segundo_nombre": "Fernanda",
|
|
384
|
+
"primer_apellido": "Navarro",
|
|
385
|
+
"segundo_apellido": "Pérez",
|
|
386
|
+
"identificacion_personal": "001-280894-0014A",
|
|
387
|
+
"genero": "Femenino",
|
|
388
|
+
"nacionalidad": "Nicaragüense",
|
|
389
|
+
"fecha_nacimiento": date(1994, 8, 28),
|
|
390
|
+
"cargo": "Recepcionista",
|
|
391
|
+
"area": "Administración",
|
|
392
|
+
"salario_base": Decimal("11000.00"),
|
|
393
|
+
"tipo_contrato": "Indefinido",
|
|
394
|
+
"fecha_alta": date.today() - timedelta(days=545), # ~1.5 years ago
|
|
395
|
+
"empresa": empresa2,
|
|
396
|
+
"correo": "lnavarro@bmo-services-demo.com",
|
|
397
|
+
"telefono": "+505 7777-6789",
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"codigo": "DEMO-EMP015",
|
|
401
|
+
"primer_nombre": "Andrés",
|
|
402
|
+
"segundo_nombre": "Mauricio",
|
|
403
|
+
"primer_apellido": "Cruz",
|
|
404
|
+
"segundo_apellido": "Aguilar",
|
|
405
|
+
"identificacion_personal": "001-170791-0015B",
|
|
406
|
+
"genero": "Masculino",
|
|
407
|
+
"nacionalidad": "Nicaragüense",
|
|
408
|
+
"fecha_nacimiento": date(1991, 7, 17),
|
|
409
|
+
"cargo": "Analista Financiero",
|
|
410
|
+
"area": "Finanzas",
|
|
411
|
+
"salario_base": Decimal("19000.00"),
|
|
412
|
+
"tipo_contrato": "Indefinido",
|
|
413
|
+
"fecha_alta": date.today() - timedelta(days=630), # ~1.7 years ago
|
|
414
|
+
"empresa": empresa2,
|
|
415
|
+
"correo": "acruz@bmo-services-demo.com",
|
|
416
|
+
"telefono": "+505 7777-7890",
|
|
417
|
+
},
|
|
418
|
+
]
|
|
419
|
+
|
|
420
|
+
empleados = []
|
|
421
|
+
for emp_data in empleados_data:
|
|
422
|
+
# Check if employee already exists
|
|
423
|
+
existing = db.session.execute(
|
|
424
|
+
db.select(Empleado).filter_by(codigo_empleado=emp_data["codigo"])
|
|
425
|
+
).scalar_one_or_none()
|
|
426
|
+
|
|
427
|
+
if existing is None:
|
|
428
|
+
empleado = Empleado()
|
|
429
|
+
empleado.codigo_empleado = emp_data["codigo"]
|
|
430
|
+
empleado.primer_nombre = emp_data["primer_nombre"]
|
|
431
|
+
empleado.segundo_nombre = emp_data.get("segundo_nombre")
|
|
432
|
+
empleado.primer_apellido = emp_data["primer_apellido"]
|
|
433
|
+
empleado.segundo_apellido = emp_data.get("segundo_apellido")
|
|
434
|
+
empleado.identificacion_personal = emp_data["identificacion_personal"]
|
|
435
|
+
empleado.genero = emp_data.get("genero")
|
|
436
|
+
empleado.nacionalidad = emp_data.get("nacionalidad")
|
|
437
|
+
empleado.tipo_identificacion = "Cédula"
|
|
438
|
+
empleado.fecha_nacimiento = emp_data.get("fecha_nacimiento")
|
|
439
|
+
empleado.fecha_alta = emp_data["fecha_alta"]
|
|
440
|
+
empleado.activo = True
|
|
441
|
+
empleado.cargo = emp_data.get("cargo")
|
|
442
|
+
empleado.area = emp_data.get("area")
|
|
443
|
+
empleado.salario_base = emp_data["salario_base"]
|
|
444
|
+
empleado.tipo_contrato = emp_data.get("tipo_contrato")
|
|
445
|
+
empleado.moneda_id = moneda.id
|
|
446
|
+
empleado.empresa_id = emp_data["empresa"].id
|
|
447
|
+
empleado.correo = emp_data.get("correo")
|
|
448
|
+
empleado.telefono = emp_data.get("telefono")
|
|
449
|
+
empleado.estado_civil = "Soltero"
|
|
450
|
+
|
|
451
|
+
db.session.add(empleado)
|
|
452
|
+
empleados.append(empleado)
|
|
453
|
+
log.trace(f"Created demo employee: {empleado.primer_nombre} {empleado.primer_apellido}")
|
|
454
|
+
else:
|
|
455
|
+
empleados.append(existing)
|
|
456
|
+
|
|
457
|
+
db.session.commit()
|
|
458
|
+
return empleados
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def load_demo_payrolls(empresa1: Empresa, empresa2: Empresa, empleados: list[Empleado]) -> tuple[Planilla, Planilla]:
|
|
462
|
+
"""Create demo payrolls with assigned employees and concepts.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
empresa1: First demo company
|
|
466
|
+
empresa2: Second demo company
|
|
467
|
+
empleados: List of demo employees
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
tuple: Two Planilla objects (planilla1, planilla2)
|
|
471
|
+
"""
|
|
472
|
+
log.trace("Loading demo payrolls...")
|
|
473
|
+
|
|
474
|
+
# Get or create required data
|
|
475
|
+
moneda = db.session.execute(db.select(Moneda).filter_by(codigo="NIO")).scalar_one_or_none()
|
|
476
|
+
|
|
477
|
+
if moneda is None:
|
|
478
|
+
moneda = db.session.execute(db.select(Moneda)).scalars().first()
|
|
479
|
+
|
|
480
|
+
tipo_planilla = db.session.execute(db.select(TipoPlanilla).filter_by(codigo="MONTHLY")).scalar_one_or_none()
|
|
481
|
+
|
|
482
|
+
if tipo_planilla is None:
|
|
483
|
+
tipo_planilla = db.session.execute(db.select(TipoPlanilla)).scalars().first()
|
|
484
|
+
|
|
485
|
+
if moneda is None or tipo_planilla is None:
|
|
486
|
+
log.trace("Required data (currency or payroll type) not available")
|
|
487
|
+
return None, None
|
|
488
|
+
|
|
489
|
+
# Create Planilla 1 for Company 1
|
|
490
|
+
planilla1 = db.session.execute(db.select(Planilla).filter_by(nombre="Planilla Demo - TechSol")).scalar_one_or_none()
|
|
491
|
+
|
|
492
|
+
if planilla1 is None:
|
|
493
|
+
planilla1 = Planilla()
|
|
494
|
+
planilla1.nombre = "Planilla Demo - TechSol"
|
|
495
|
+
planilla1.descripcion = "Planilla de demostración para Tecnología y Soluciones S.A."
|
|
496
|
+
planilla1.tipo_planilla_id = tipo_planilla.id
|
|
497
|
+
planilla1.moneda_id = moneda.id
|
|
498
|
+
planilla1.empresa_id = empresa1.id
|
|
499
|
+
planilla1.activo = True
|
|
500
|
+
db.session.add(planilla1)
|
|
501
|
+
log.trace("Created demo payroll: Planilla Demo - TechSol")
|
|
502
|
+
|
|
503
|
+
# Create Planilla 2 for Company 2
|
|
504
|
+
planilla2 = db.session.execute(
|
|
505
|
+
db.select(Planilla).filter_by(nombre="Planilla Demo - BMO Services")
|
|
506
|
+
).scalar_one_or_none()
|
|
507
|
+
|
|
508
|
+
if planilla2 is None:
|
|
509
|
+
planilla2 = Planilla()
|
|
510
|
+
planilla2.nombre = "Planilla Demo - BMO Services"
|
|
511
|
+
planilla2.descripcion = "Planilla de demostración para Servicios Profesionales BMO Ltda."
|
|
512
|
+
planilla2.tipo_planilla_id = tipo_planilla.id
|
|
513
|
+
planilla2.moneda_id = moneda.id
|
|
514
|
+
planilla2.empresa_id = empresa2.id
|
|
515
|
+
planilla2.activo = True
|
|
516
|
+
db.session.add(planilla2)
|
|
517
|
+
log.trace("Created demo payroll: Planilla Demo - BMO Services")
|
|
518
|
+
|
|
519
|
+
db.session.commit()
|
|
520
|
+
|
|
521
|
+
# Assign employees to payrolls
|
|
522
|
+
for empleado in empleados:
|
|
523
|
+
if empleado.empresa_id == empresa1.id:
|
|
524
|
+
planilla = planilla1
|
|
525
|
+
elif empleado.empresa_id == empresa2.id:
|
|
526
|
+
planilla = planilla2
|
|
527
|
+
else:
|
|
528
|
+
continue
|
|
529
|
+
|
|
530
|
+
# Check if already assigned
|
|
531
|
+
existing = db.session.execute(
|
|
532
|
+
db.select(PlanillaEmpleado).filter_by(planilla_id=planilla.id, empleado_id=empleado.id)
|
|
533
|
+
).scalar_one_or_none()
|
|
534
|
+
|
|
535
|
+
if existing is None:
|
|
536
|
+
asignacion = PlanillaEmpleado()
|
|
537
|
+
asignacion.planilla_id = planilla.id
|
|
538
|
+
asignacion.empleado_id = empleado.id
|
|
539
|
+
asignacion.activo = True
|
|
540
|
+
asignacion.fecha_inicio = empleado.fecha_alta
|
|
541
|
+
db.session.add(asignacion)
|
|
542
|
+
log.trace(f"Assigned employee {empleado.codigo_empleado} to payroll {planilla.nombre}")
|
|
543
|
+
|
|
544
|
+
db.session.commit()
|
|
545
|
+
|
|
546
|
+
# Assign perceptions, deductions, and benefits
|
|
547
|
+
_assign_concepts_to_payroll(planilla1)
|
|
548
|
+
_assign_concepts_to_payroll(planilla2)
|
|
549
|
+
|
|
550
|
+
return planilla1, planilla2
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _assign_concepts_to_payroll(planilla: Planilla) -> None:
|
|
554
|
+
"""Assign common perceptions, deductions, and benefits to a payroll.
|
|
555
|
+
|
|
556
|
+
Args:
|
|
557
|
+
planilla: Planilla to assign concepts to
|
|
558
|
+
"""
|
|
559
|
+
# Get some common concepts
|
|
560
|
+
percepciones = (
|
|
561
|
+
db.session.execute(
|
|
562
|
+
db.select(Percepcion).filter(Percepcion.codigo.in_(["OVERTIME", "BONUSES", "TRANSPORT_ALLOWANCE"]))
|
|
563
|
+
)
|
|
564
|
+
.scalars()
|
|
565
|
+
.all()
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
for percepcion in percepciones:
|
|
569
|
+
existing = db.session.execute(
|
|
570
|
+
db.select(PlanillaIngreso).filter_by(planilla_id=planilla.id, percepcion_id=percepcion.id)
|
|
571
|
+
).scalar_one_or_none()
|
|
572
|
+
|
|
573
|
+
if existing is None:
|
|
574
|
+
asignacion = PlanillaIngreso()
|
|
575
|
+
asignacion.planilla_id = planilla.id
|
|
576
|
+
asignacion.percepcion_id = percepcion.id
|
|
577
|
+
asignacion.activo = True
|
|
578
|
+
asignacion.editable = True
|
|
579
|
+
db.session.add(asignacion)
|
|
580
|
+
|
|
581
|
+
# Assign deductions
|
|
582
|
+
deducciones = (
|
|
583
|
+
db.session.execute(
|
|
584
|
+
db.select(Deduccion).filter(Deduccion.codigo.in_(["UNPAID_ABSENCES", "TARDINESS", "INTERNAL_LOANS"]))
|
|
585
|
+
)
|
|
586
|
+
.scalars()
|
|
587
|
+
.all()
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
for idx, deduccion in enumerate(deducciones):
|
|
591
|
+
existing = db.session.execute(
|
|
592
|
+
db.select(PlanillaDeduccion).filter_by(planilla_id=planilla.id, deduccion_id=deduccion.id)
|
|
593
|
+
).scalar_one_or_none()
|
|
594
|
+
|
|
595
|
+
if existing is None:
|
|
596
|
+
asignacion = PlanillaDeduccion()
|
|
597
|
+
asignacion.planilla_id = planilla.id
|
|
598
|
+
asignacion.deduccion_id = deduccion.id
|
|
599
|
+
asignacion.prioridad = 300 + (idx * 10) # Set priority
|
|
600
|
+
asignacion.activo = True
|
|
601
|
+
asignacion.editable = True
|
|
602
|
+
db.session.add(asignacion)
|
|
603
|
+
|
|
604
|
+
# Assign benefits
|
|
605
|
+
prestaciones = (
|
|
606
|
+
db.session.execute(
|
|
607
|
+
db.select(Prestacion).filter(
|
|
608
|
+
Prestacion.codigo.in_(["PAID_VACATION_PROVISION", "THIRTEENTH_SALARY_PROVISION"])
|
|
609
|
+
)
|
|
610
|
+
)
|
|
611
|
+
.scalars()
|
|
612
|
+
.all()
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
for prestacion in prestaciones:
|
|
616
|
+
existing = db.session.execute(
|
|
617
|
+
db.select(PlanillaPrestacion).filter_by(planilla_id=planilla.id, prestacion_id=prestacion.id)
|
|
618
|
+
).scalar_one_or_none()
|
|
619
|
+
|
|
620
|
+
if existing is None:
|
|
621
|
+
asignacion = PlanillaPrestacion()
|
|
622
|
+
asignacion.planilla_id = planilla.id
|
|
623
|
+
asignacion.prestacion_id = prestacion.id
|
|
624
|
+
asignacion.activo = True
|
|
625
|
+
asignacion.editable = True
|
|
626
|
+
db.session.add(asignacion)
|
|
627
|
+
|
|
628
|
+
db.session.commit()
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def create_demo_nomina(planilla: Planilla) -> Nomina | None:
|
|
632
|
+
"""Create a demo payroll run for next month.
|
|
633
|
+
|
|
634
|
+
Creates a nomina with dynamically generated dates to ensure it always
|
|
635
|
+
occurs in the month immediately following the initial application setup date.
|
|
636
|
+
|
|
637
|
+
Args:
|
|
638
|
+
planilla: Planilla to create nomina for
|
|
639
|
+
|
|
640
|
+
Returns:
|
|
641
|
+
Nomina object or None if creation failed
|
|
642
|
+
"""
|
|
643
|
+
log.trace(f"Creating demo nomina for payroll: {planilla.nombre}")
|
|
644
|
+
|
|
645
|
+
# Calculate dates for next month
|
|
646
|
+
today = date.today()
|
|
647
|
+
next_month_start = today.replace(day=1) + relativedelta(months=1)
|
|
648
|
+
next_month_end = (next_month_start + relativedelta(months=1)) - timedelta(days=1)
|
|
649
|
+
|
|
650
|
+
# Check if demo nomina already exists for this period
|
|
651
|
+
existing = db.session.execute(
|
|
652
|
+
db.select(Nomina).filter_by(
|
|
653
|
+
planilla_id=planilla.id, periodo_inicio=next_month_start, periodo_fin=next_month_end
|
|
654
|
+
)
|
|
655
|
+
).scalar_one_or_none()
|
|
656
|
+
|
|
657
|
+
if existing is not None:
|
|
658
|
+
log.trace(f"Demo nomina already exists for period {next_month_start} to {next_month_end}")
|
|
659
|
+
return existing
|
|
660
|
+
|
|
661
|
+
# Create nomina
|
|
662
|
+
nomina = Nomina()
|
|
663
|
+
nomina.planilla_id = planilla.id
|
|
664
|
+
nomina.periodo_inicio = next_month_start
|
|
665
|
+
nomina.periodo_fin = next_month_end
|
|
666
|
+
nomina.estado = "generado"
|
|
667
|
+
nomina.total_bruto = Decimal("0.00")
|
|
668
|
+
nomina.total_deducciones = Decimal("0.00")
|
|
669
|
+
nomina.total_neto = Decimal("0.00")
|
|
670
|
+
db.session.add(nomina)
|
|
671
|
+
db.session.commit()
|
|
672
|
+
|
|
673
|
+
log.trace(f"Created demo nomina for period {next_month_start} to {next_month_end}")
|
|
674
|
+
return nomina
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def create_demo_novelties(empleados: list[Empleado]) -> None:
|
|
678
|
+
"""Create demo novelties (overtime, absences) for employees.
|
|
679
|
+
|
|
680
|
+
Creates various types of novelties to demonstrate the system's
|
|
681
|
+
capability to handle employee-specific adjustments.
|
|
682
|
+
|
|
683
|
+
Args:
|
|
684
|
+
empleados: List of employees to create novelties for
|
|
685
|
+
"""
|
|
686
|
+
log.trace("Creating demo novelties...")
|
|
687
|
+
|
|
688
|
+
if len(empleados) < 5:
|
|
689
|
+
log.trace("Not enough employees to create demo novelties")
|
|
690
|
+
return
|
|
691
|
+
|
|
692
|
+
# Get perceptions for overtime
|
|
693
|
+
overtime = db.session.execute(db.select(Percepcion).filter_by(codigo="OVERTIME")).scalar_one_or_none()
|
|
694
|
+
|
|
695
|
+
# Get deduction for absences
|
|
696
|
+
absence = db.session.execute(db.select(Deduccion).filter_by(codigo="UNPAID_ABSENCES")).scalar_one_or_none()
|
|
697
|
+
|
|
698
|
+
# Get a demo nomina to associate novelties with
|
|
699
|
+
# (We'll use the first available nomina or None)
|
|
700
|
+
nomina = db.session.execute(db.select(Nomina)).scalars().first()
|
|
701
|
+
|
|
702
|
+
if nomina is None:
|
|
703
|
+
log.trace("No nomina available to associate novelties")
|
|
704
|
+
# Novelties can still be created without a nomina (as pending)
|
|
705
|
+
|
|
706
|
+
# Create overtime for some employees
|
|
707
|
+
if overtime:
|
|
708
|
+
for i in [0, 1, 4]: # First, second, and fifth employee
|
|
709
|
+
if i < len(empleados):
|
|
710
|
+
empleado = empleados[i]
|
|
711
|
+
|
|
712
|
+
# Check if novedad already exists
|
|
713
|
+
if nomina:
|
|
714
|
+
existing = db.session.execute(
|
|
715
|
+
db.select(NominaNovedad).filter_by(
|
|
716
|
+
nomina_id=nomina.id, empleado_id=empleado.id, codigo_concepto=overtime.codigo
|
|
717
|
+
)
|
|
718
|
+
).scalar_one_or_none()
|
|
719
|
+
|
|
720
|
+
if existing is None:
|
|
721
|
+
novedad = NominaNovedad()
|
|
722
|
+
novedad.nomina_id = nomina.id
|
|
723
|
+
novedad.empleado_id = empleado.id
|
|
724
|
+
novedad.tipo_valor = "horas"
|
|
725
|
+
novedad.codigo_concepto = overtime.codigo
|
|
726
|
+
novedad.valor_cantidad = Decimal("8.0") # 8 hours overtime
|
|
727
|
+
novedad.fecha_novedad = date.today() - timedelta(days=5)
|
|
728
|
+
novedad.percepcion_id = overtime.id
|
|
729
|
+
novedad.estado = "pendiente"
|
|
730
|
+
db.session.add(novedad)
|
|
731
|
+
log.trace(f"Created overtime novedad for {empleado.codigo_empleado}")
|
|
732
|
+
|
|
733
|
+
# Create absences for some employees
|
|
734
|
+
if absence:
|
|
735
|
+
for i in [2, 3]: # Third and fourth employee
|
|
736
|
+
if i < len(empleados):
|
|
737
|
+
empleado = empleados[i]
|
|
738
|
+
|
|
739
|
+
# Check if novedad already exists
|
|
740
|
+
if nomina:
|
|
741
|
+
existing = db.session.execute(
|
|
742
|
+
db.select(NominaNovedad).filter_by(
|
|
743
|
+
nomina_id=nomina.id, empleado_id=empleado.id, codigo_concepto=absence.codigo
|
|
744
|
+
)
|
|
745
|
+
).scalar_one_or_none()
|
|
746
|
+
|
|
747
|
+
if existing is None:
|
|
748
|
+
novedad = NominaNovedad()
|
|
749
|
+
novedad.nomina_id = nomina.id
|
|
750
|
+
novedad.empleado_id = empleado.id
|
|
751
|
+
novedad.tipo_valor = "dias"
|
|
752
|
+
novedad.codigo_concepto = absence.codigo
|
|
753
|
+
novedad.valor_cantidad = Decimal("1.0") # 1 day absence
|
|
754
|
+
novedad.fecha_novedad = date.today() - timedelta(days=3)
|
|
755
|
+
novedad.deduccion_id = absence.id
|
|
756
|
+
novedad.estado = "pendiente"
|
|
757
|
+
db.session.add(novedad)
|
|
758
|
+
log.trace(f"Created absence novedad for {empleado.codigo_empleado}")
|
|
759
|
+
|
|
760
|
+
db.session.commit()
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def load_demo_data() -> None:
|
|
764
|
+
"""Load all demo data into the database.
|
|
765
|
+
|
|
766
|
+
This function orchestrates the loading of comprehensive demo data:
|
|
767
|
+
1. Creates demo companies
|
|
768
|
+
2. Creates demo employees
|
|
769
|
+
3. Creates demo payrolls and assigns employees
|
|
770
|
+
4. Assigns perceptions, deductions, and benefits
|
|
771
|
+
5. Creates a demo nomina for next month
|
|
772
|
+
6. Creates demo novelties
|
|
773
|
+
|
|
774
|
+
This function is idempotent - safe to call multiple times.
|
|
775
|
+
"""
|
|
776
|
+
try:
|
|
777
|
+
log.trace("=" * 60)
|
|
778
|
+
log.trace("Loading demo data for manual testing...")
|
|
779
|
+
log.trace("=" * 60)
|
|
780
|
+
|
|
781
|
+
# 1. Create demo companies
|
|
782
|
+
empresa1, empresa2 = load_demo_companies()
|
|
783
|
+
|
|
784
|
+
# 2. Create demo employees
|
|
785
|
+
empleados = load_demo_employees(empresa1, empresa2)
|
|
786
|
+
|
|
787
|
+
if not empleados:
|
|
788
|
+
log.trace("No employees created, skipping payroll and nomina creation")
|
|
789
|
+
return
|
|
790
|
+
|
|
791
|
+
# 3. Create demo payrolls with employees and concepts
|
|
792
|
+
planilla1, planilla2 = load_demo_payrolls(empresa1, empresa2, empleados)
|
|
793
|
+
|
|
794
|
+
# 4. Create demo nomina for next month (for first payroll)
|
|
795
|
+
if planilla1:
|
|
796
|
+
create_demo_nomina(planilla1)
|
|
797
|
+
|
|
798
|
+
# 5. Create demo novelties
|
|
799
|
+
create_demo_novelties(empleados)
|
|
800
|
+
|
|
801
|
+
log.trace("=" * 60)
|
|
802
|
+
log.trace("Demo data loading completed successfully!")
|
|
803
|
+
log.trace("=" * 60)
|
|
804
|
+
log.trace(f"Created {len(empleados)} demo employees")
|
|
805
|
+
log.trace("Created 2 demo companies")
|
|
806
|
+
log.trace("Created 2 demo payrolls with assigned concepts")
|
|
807
|
+
log.trace("Created demo novelties (overtime, absences)")
|
|
808
|
+
log.trace("=" * 60)
|
|
809
|
+
|
|
810
|
+
except Exception as exc:
|
|
811
|
+
log.error(f"Error loading demo data: {exc}")
|
|
812
|
+
log.exception("Demo data loading exception")
|
|
813
|
+
db.session.rollback()
|