definition limit uvt, and 60/40

This commit is contained in:
Wilson Gomez 2023-02-01 17:25:10 -05:00
parent b321a5428a
commit 75f6302553
7 changed files with 134 additions and 118 deletions

View File

@ -1,5 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
PAYMENTS = [
'salary', 'bonus', 'reco', 'recf', 'hedo', 'heno', 'dom', 'hedf', 'henf',
@ -10,12 +11,13 @@ SOCIAL_SEGURITY = [
]
LIM_UVT_DEDUCTIBLE = {
'fvp_ind': 2500,
'afc_fvp': (3800/12),
'fvp_ind': Decimal(2500/12),
'afc_fvp': Decimal(3800/12),
'housing_interest': 100,
'health_prepaid': 16,
'dependents': 32,
'exempted_incom': 240
'dependents': 72,
'exempted_incom': Decimal(790/12),
'renta_deductions': Decimal(1340/12)
}
LIM_PERCENT_DEDUCTIBLE = {

View File

@ -934,7 +934,6 @@ class LiquidationGroup(Wizard):
contracts = Contract.search([
('employee', '=', employee.id),
('id', 'not in', contract_ids),
('id', 'not in', contract_ids),
])
if not contracts:
continue

View File

@ -6,6 +6,8 @@ from decimal import Decimal
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta
import math
from operator import attrgetter
from functools import lru_cache
from trytond.exceptions import UserError
from trytond.model import ModelView, fields, Workflow, ModelSQL
@ -48,16 +50,20 @@ class PayrollLine(metaclass=PoolMeta):
move_lines = fields.Many2Many('staff.payroll.line-move.line',
'line', 'move_line', 'Payroll Line - Move Line')
origin = fields.Reference("Origin", selection='get_origin')
amount_60_40 = fields.Numeric('Amount 60/40', digits=(16, 2),
depends=['wage_type'])
def get_expense_amount(self):
expense = super(PayrollLine, self).get_expense_amount()
if self.wage_type.round_amounts:
if self.wage_type.round_amounts == 'above_amount':
expense = math.floor(float(expense) / 100.0) * 100
elif self.wage_type.round_amounts == 'under_amount':
expense = math.floor(float(expense) / 100.0) * 100
elif self.wage_type.round_amounts == 'automatic':
expense = round(expense, -2)
round_amounts = self.wage_type.round_amounts
if not round_amounts:
return expense
if round_amounts == 'above_amount':
expense = math.floor(float(expense) / 100.0) * 100
elif round_amounts == 'under_amount':
expense = math.floor(float(expense) / 100.0) * 100
elif round_amounts == 'automatic':
expense = round(expense, -2)
return expense
@classmethod
@ -245,7 +251,7 @@ class Payroll(metaclass=PoolMeta):
def _get_line_quantity(self, quantity_days, wage, extras, discount):
Configuration = Pool().get('staff.configuration')
config = Configuration(1)
quantity_days = self.get_days(self.start, self.end, wage)
quantity_days = self.get_days(self.start, self.end, wage.adjust_days_worked)
quantity = super(Payroll, self)._get_line_quantity(
quantity_days, wage, extras, discount
)
@ -255,60 +261,69 @@ class Payroll(metaclass=PoolMeta):
return quantity
@fields.depends('start', 'end', 'employee', 'period', 'contract')
def get_days(self, start, end, wage=None):
@lru_cache(maxsize=20)
def get_days(self, start, end, wage_adjust_days_worked=None):
adjust = 1
adjust_days_worked = False
if self.contract and self.contract.position:
adjust_days_worked = self.contract.position.adjust_days_worked
else:
if self.employee.position:
adjust_days_worked = self.employee.position.adjust_days_worked
if (adjust_days_worked) or (wage and wage.adjust_days_worked):
if end.day == 31 or (end.month == 2
and (end.day == 28 or end.day == 29)):
adjust = 31 - end.day
# adjust_days_worked = False
adjust_days_worked = attrgetter('position.adjust_days_worked')(self.contract)
# print('this is', adj)
# if self.contract and self.contract.position:
# adjust_days_worked = self.contract.position.adjust_days_worked
# else:
# if self.employee.position:
# adjust_days_worked = self.employee.position.adjust_days_worked
end_month, end_day = attrgetter('month', 'day')(end)
if (adjust_days_worked) or (wage_adjust_days_worked):
if end_day == 31 or (end_month == 2
and (end_day == 28 or end_day == 29)):
adjust = 31 - end_day
quantity_days = (end - start).days + adjust
if quantity_days < 0:
quantity_days = 0
if start == end:
quantity_days = 1
if Transaction().context.get('absenteeism_days'):
quantity_days = quantity_days - Transaction().context.get('absenteeism_days')
absenteeism_days = Transaction().context.get('absenteeism_days')
if absenteeism_days:
quantity_days = quantity_days - absenteeism_days
return quantity_days
def get_salary_full(self, wage):
res = super(Payroll, self).get_salary_full(wage)
salary_pae = _ZERO
# salary_pae = _ZERO
salary_full = res['salary']
concepts = ('health', 'retirement', 'risk', 'box_family')
if wage.type_concept in concepts:
salary_full += sum(line.amount_60_40 for line in self.lines if line.amount_60_40)
if wage.month_application:
if self._is_last_payroll_month():
salary_full_month = self.search_salary_month(wage)
salary_full = salary_full_month + salary_pae
elif self.last_payroll:
salary_full = salary_full + salary_pae
salary_full = salary_full_month # + salary_pae
# elif self.last_payroll:
# salary_full = salary_full + salary_pae
else:
salary_full = _ZERO
else:
salary_full += salary_pae
# else:
# salary_full += salary_pae
salary_full = self.validate_minimal_amount(wage, salary_full)
return {'salary': salary_full}
def validate_minimal_amount(self, wage, amount):
if wage.minimal_amount:
if amount < wage.minimal_amount:
return 0
min_amount = wage.minimal_amount
if min_amount and amount < min_amount:
return 0
return amount
@lru_cache(maxsize=20)
def is_first_payroll(self):
if self.start <= self.contract.start_date <= self.end:
return True
return False
@lru_cache(maxsize=20)
def _is_last_payroll_month(self):
_, end_day = calendar.monthrange(self.start.year, self.start.month)
last_day = date(self.start.year, self.start.month, end_day)
year, month = attrgetter('year', 'month')(self.start)
_, end_day = calendar.monthrange(year, month)
last_day = date(year, month, end_day)
if last_day == self.period.end:
return True
return False
@ -383,8 +398,6 @@ class Payroll(metaclass=PoolMeta):
self.process_loans_to_pay(LoanLine, PayrollLine, MoveLine)
for line in self.lines:
to_write = {}
# if line.wage_type.type_concept == 'loan':
# self.process_loans_to_pay(line, LoanLine, PayrollLine, MoveLine)
if line.wage_type.provision_cancellation:
amount_line = [m.amount for m in self.lines if m.wage_type
@ -419,7 +432,6 @@ class Payroll(metaclass=PoolMeta):
PayrollLine.write([line], to_write)
def process_loans_to_pay(self, LoanLine, PayrollLine, MoveLine):
dom = [
('loan.party', '=', self.employee.party.id),
('loan.wage_type', '!=', None),
@ -593,6 +605,8 @@ class Payroll(metaclass=PoolMeta):
def get_salary_average(cls, end_date, employee, contract, wage):
# end_date = self.start
start_date_contract = contract.start_date
if not contract.variable_salary:
return Decimal(str(round(contract.salary / 30 if contract.salary else 0, 2)))
if start_date_contract.month != end_date.month:
start_date = end_date + relativedelta(months=-3)
@ -626,13 +640,15 @@ class Payroll(metaclass=PoolMeta):
def update_wage_no_salary(self):
PayrollLine = Pool().get('staff.payroll.line')
att_getter = attrgetter('wage_type.type_concept', 'wage_type.month_application')
for line in self.lines:
if line.wage_type.month_application:
type_concept, month_application = att_getter(line)
if month_application:
salary_args = self.get_salary_full(line.wage_type)
unit_value = line.wage_type.compute_unit_price(salary_args)
if line.wage_type.type_concept == 'interest':
if type_concept == 'interest':
unit_value = self._compute_interest(line.wage_type, self.start)
elif line.wage_type.type_concept == 'tax':
elif type_concept == 'tax':
unit_value = self._compute_line_tax(line)
else:
continue
@ -656,16 +672,19 @@ class Payroll(metaclass=PoolMeta):
return [p.id for p in payrolls]
def _compute_interest(self, wage_type, limit_date=False, name=None):
start = date(self.start.year, 1, 1)
end = date(self.start.year, 12, 31)
year = self.start.year
start = date(year, 1, 1)
end = date(year, 12, 31)
concepts_salary_ids = [wt.id for wt in wage_type.concepts_salary]
start_date, contract_id, employee_id = attrgetter(
'contract.start_date', 'contract.id', 'employee.id')(self)
if self.contract.start_date > start:
start = self.contract.start_date
if start_date > start:
start = start_date
dom_payrolls = [
('start', '>=', start),
('contract', '=', self.contract.id),
('employee', '=', self.employee.id),
('contract', '=', contract_id),
('employee', '=', employee_id),
]
if limit_date:
dom_payrolls.append(('start', '<=', limit_date))
@ -675,7 +694,7 @@ class Payroll(metaclass=PoolMeta):
payrolls = self.search(dom_payrolls)
payrolls_ids = [p.id for p in payrolls]
time_contracting = sum([p.worked_days for p in payrolls])
time_contracting = sum(p.worked_days for p in payrolls)
salary_hoard = float(self._compute_hoard(
payrolls_ids,
@ -706,52 +725,59 @@ class Payroll(metaclass=PoolMeta):
('wage_type', 'in', wages_ids),
('payroll', 'in', payrolls_ids),
])
return sum([l.amount for l in lines if not l.reconciled])
return sum(pl.amount for pl in lines if not pl.reconciled)
def recompute_lines(self):
PayrollLine = Pool().get('staff.payroll.line')
amounts_60_40 = []
amounts_60_40_app = amounts_60_40.append
wages_60_40 = []
wages_60_40_app = wages_60_40.append
att_getter = attrgetter(
'amount', 'wage_type.definition',
'wage_type.account_60_40',
'wage_type.salary_constitute')
for line in self.lines:
wage = line.wage_type
if wage.definition == 'payment':
if wage.salary_constitute:
amounts_60_40.append(line.amount)
elif wage.wage_type_60_40:
wages_60_40.append(line)
amounts_60_40.append(line.amount)
amount_wages_60_40 = sum([l.amount for l in wages_60_40])
amount_to_validate = sum(amounts_60_40) * Decimal(0.4) < amount_wages_60_40
amount, definition, account_60_40, salary_constitute = att_getter(line)
if definition == 'payment':
if salary_constitute:
amounts_60_40_app(amount)
elif account_60_40:
wages_60_40_app(line)
amounts_60_40_app(amount)
amount_wages_60_40 = sum(pl.amount for pl in wages_60_40)
fourty_percentage_amount = sum(amounts_60_40) * Decimal(0.4)
amount_to_validate = fourty_percentage_amount < amount_wages_60_40
if amounts_60_40 and wages_60_40 and amount_to_validate:
amount_dif = amount_wages_60_40 - sum(amounts_60_40) * Decimal(0.4)
lines_to_create = []
for l in wages_60_40:
new_unit_value = Decimal(str(round((amount_dif * (l.amount/amount_wages_60_40)),2)))
unit_value = Decimal(str(round((l.amount - new_unit_value), 2)))
lines_to_create.append(self.get_line(l.wage_type.wage_type_60_40, 1, new_unit_value))
PayrollLine.write([l], {
'unit_value': unit_value,
amount_dif = amount_wages_60_40 - fourty_percentage_amount
for wl in wages_60_40:
new_unit_value = Decimal(str(round(
(amount_dif * (wl.amount / amount_wages_60_40)), 2)))
# unit_value = Decimal(str(round((l.amount - new_unit_value), 2)))
# print(new_unit_value, 'this is unit value')
PayrollLine.write([wl], {
# 'unit_value': unit_value,
'amount_60_40': new_unit_value,
})
PayrollLine.create(lines_to_create)
super(Payroll, self).recompute_lines()
self.update_wage_no_salary()
line_tax = None
deductions = _ZERO
# deductions = _ZERO
for line in self.lines:
if line.wage_type.provision_cancellation:
w_provision_cancellation = line.wage_type.provision_cancellation
if w_provision_cancellation:
amount_line = [m.amount for m in self.lines if m.wage_type
== line.wage_type.provision_cancellation]
== w_provision_cancellation]
values = []
for m in line.move_lines:
values.append(abs(m.debit - m.credit))
amount = sum(values) + sum(amount_line)
# 'amount': amount,
line.write([line], {
'unit_value': amount,
})
if line.wage_type.definition == 'deduction':
deductions += line.amount
if line.wage_type.type_concept == 'tax':
# elif line.wage_type.definition == 'deduction':
# deductions += line.amount
elif line.wage_type.type_concept == 'tax':
line_tax = line
if line_tax:
unit_value = self._compute_line_tax(line_tax)
@ -771,7 +797,7 @@ class Payroll(metaclass=PoolMeta):
) else None
res = 0
if field == 'dependents' and value_field:
value_percent = lim_percent * value_field * base
value_percent = lim_percent * value_field/100 * base
value_uvt = lim_uvt * uvt_config
res = min(value_percent, value_uvt)
else:
@ -794,38 +820,19 @@ class Payroll(metaclass=PoolMeta):
salary_full = salary_full['salary']
if self.last_payroll:
payrolls_ids = self._get_payrolls_contract()
# This apply in liquidation document
# WageType = Pool().get('staff.wage_type')
# wage_types = WageType.search([
# ('type_concept', '=', 'holidays')
# ])
# holidays_ids = [w.id for w in wage_types]
# hoard_holidays = self._compute_hoard(
# payrolls_ids,
# holidays_ids
# )
# salary_full += hoard_holidays
deductions_fields = [
'fvp_ind', 'health_prepaid', 'fvp', 'afc', 'other_income',
'housing_interest', 'dependents'
'fvp_ind', 'health_prepaid', 'fvp', 'afc',
'housing_interest', 'dependents', 'other_income',
]
attrs = attrgetter(*deductions_fields)(self.employee)
deductions_values = {
'fvp_ind': 0,
'health_prepaid': 0,
'fvp_afc': 0,
'other_income': 0,
'housing_interest': 0,
'dependents': 0
'fvp_ind': (attrs[0] or _ZERO),
'health_prepaid': (attrs[1] or _ZERO),
'fvp_afc': (attrs[2] or _ZERO) + (attrs[3] or _ZERO),
'housing_interest': (attrs[4] or _ZERO),
'dependents': (attrs[5] or _ZERO),
'other_income': (attrs[6] or _ZERO),
}
for field in deductions_fields:
value = getattr(self.employee, field) if getattr(
self.employee, field) else 0
if field not in ('fvp', 'afc'):
deductions_values[field] = value
elif field in ('fvp', 'afc'):
deductions_values['fvp_afc'] += value
value = 0
for k, v in deductions_values.items():
res = self.check_limit(salary_full, k, v)
deductions_values[k] = res
@ -854,18 +861,14 @@ class Payroll(metaclass=PoolMeta):
def get_non_fiscal_amount(self, name=None):
res = _ZERO
concept = name[:-7]
for line in self.lines:
if line.wage_type.type_concept == concept:
res += line.amount
res = sum(pl.amount for pl in self.lines if pl.wage_type.type_concept == concept)
return res
def get_deductions_month(self):
payrolls = self._get_payrolls_month()
sum_deductions = _ZERO
for payroll in payrolls:
for line in payroll.lines:
if line.wage_type.definition == 'deduction':
sum_deductions += line.amount
sum_deductions = sum(pl.amount for p in payrolls for pl in p.lines \
if pl.wage_type.definition == 'deduction')
return sum_deductions
@fields.depends('lines')

View File

@ -1,5 +1,5 @@
[tryton]
version=6.0.5
version=6.0.6
depends:
company
account

View File

@ -11,6 +11,10 @@ this repository contains the full copyright notices and license terms. -->
</page>
</notebook>
</xpath>
<xpath expr="/form/label[@name='party']" position="before">
<label name="amount_60_40"/>
<field name="amount_60_40"/>
</xpath>
<xpath expr="/form/field[@name='party']" position="after">
<label name="origin"/>
<field name="origin"/>

View File

@ -19,7 +19,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="round_amounts"/>
<label name="provision_cancellation"/>
<field name="provision_cancellation"/>
<label name="wage_type_60_40"/>
<field name="wage_type_60_40"/>
<label name="account_60_40"/>
<field name="account_60_40"/>
</xpath>
</data>

View File

@ -4,6 +4,7 @@ from trytond.pool import PoolMeta
from trytond.model import fields
from trytond.transaction import Transaction
from trytond.report import Report
from trytond.pyson import Eval
ROUND_AMOUNTS = [
('', ''),
@ -25,7 +26,14 @@ class WageType(metaclass=PoolMeta):
round_amounts = fields.Selection(ROUND_AMOUNTS, 'Rounds amounts',
help='Approximates the highest value in hundreds')
provision_cancellation = fields.Many2One('staff.wage_type', 'Cancellation of Provision')
wage_type_60_40 = fields.Many2One('staff.wage_type', 'Wage Type 60/40', help='select wage type for set 60/40')
# wage_type_60_40 = fields.Many2One('staff.wage_type', 'Wage Type 60/40', help='select wage type for set 60/40')
account_60_40 = fields.Many2One('account.account', 'Account 60/40',
domain=[
('type', '!=', None),
('company', '=', Eval('context', {}).get('company', 0))],
states={
'invisible': ~Eval('context', {}).get('company'),
}, help='select wage type for set 60/40')
@classmethod
def __setup__(cls):