definition limit uvt, and 60/40
This commit is contained in:
parent
b321a5428a
commit
75f6302553
10
constants.py
10
constants.py
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
221
payroll.py
221
payroll.py
|
@ -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')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[tryton]
|
||||
version=6.0.5
|
||||
version=6.0.6
|
||||
depends:
|
||||
company
|
||||
account
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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>
|
||||
|
|
10
wage_type.py
10
wage_type.py
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue