2020-11-04 02:24:58 +01:00
|
|
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
|
|
# this repository contains the full copyright notices and license terms.
|
2020-04-16 00:38:42 +02:00
|
|
|
from decimal import Decimal
|
|
|
|
from trytond.model import Workflow, ModelSQL, ModelView, fields
|
|
|
|
from trytond.pool import Pool
|
|
|
|
from trytond.report import Report
|
|
|
|
from trytond.pyson import Eval, If, Bool
|
2021-02-23 22:47:31 +01:00
|
|
|
from trytond.wizard import Wizard, StateView, Button, StateTransition, StateReport
|
2020-04-16 00:38:42 +02:00
|
|
|
from trytond.transaction import Transaction
|
2021-06-09 18:07:42 +02:00
|
|
|
from trytond.i18n import gettext
|
2021-08-13 03:41:35 +02:00
|
|
|
from .exceptions import (
|
|
|
|
LiquidationEmployeeError, MissingSecuenceLiquidation,
|
2022-05-03 00:46:19 +02:00
|
|
|
LiquidationMoveError, WageTypeConceptError, LiquidationDeleteError,
|
|
|
|
RecordDuplicateError
|
2021-08-13 03:41:35 +02:00
|
|
|
)
|
2020-04-16 00:38:42 +02:00
|
|
|
|
2020-11-04 02:24:58 +01:00
|
|
|
STATES = {'readonly': (Eval('state') != 'draft')}
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
_ZERO = Decimal('0.0')
|
|
|
|
|
|
|
|
BONUS_SERVICE = ['bonus_service']
|
2020-11-04 04:34:32 +01:00
|
|
|
|
2020-11-04 02:24:58 +01:00
|
|
|
CONTRACT = [
|
|
|
|
'bonus_service', 'health', 'retirement', 'unemployment', 'interest',
|
2022-01-19 20:29:53 +01:00
|
|
|
'holidays', 'convencional_bonus'
|
2022-01-19 14:24:14 +01:00
|
|
|
]
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
class Liquidation(Workflow, ModelSQL, ModelView):
|
|
|
|
'Staff Liquidation'
|
|
|
|
__name__ = 'staff.liquidation'
|
|
|
|
number = fields.Char('Number', readonly=True, help="Secuence",
|
|
|
|
select=True)
|
|
|
|
employee = fields.Many2One('company.employee', 'Employee',
|
|
|
|
states=STATES, required=True, depends=['state'])
|
|
|
|
start_period = fields.Many2One('staff.payroll.period', 'Start Period',
|
|
|
|
required=True, states=STATES)
|
|
|
|
end_period = fields.Many2One('staff.payroll.period', 'End Period',
|
|
|
|
required=True, states=STATES, depends=['start_period'])
|
|
|
|
kind = fields.Selection([
|
|
|
|
('contract', 'Contract'),
|
|
|
|
('bonus_service', 'Bonus Service'),
|
|
|
|
('interest', 'Interest'),
|
2021-02-04 16:45:22 +01:00
|
|
|
('unemployment', 'Unemployment'),
|
2022-01-19 20:34:22 +01:00
|
|
|
('holidays', 'Vacation'),
|
2022-01-19 14:24:14 +01:00
|
|
|
('convencional_bonus', 'Convencional Bonus'),
|
2020-04-16 00:38:42 +02:00
|
|
|
], 'Kind', required=True, states=STATES)
|
|
|
|
liquidation_date = fields.Date('Liquidation Date', states=STATES,
|
|
|
|
required=True)
|
|
|
|
lines = fields.One2Many('staff.liquidation.line', 'liquidation',
|
|
|
|
'Lines', states=STATES, depends=['employee', 'state'])
|
|
|
|
gross_payments = fields.Function(fields.Numeric(
|
|
|
|
'Gross Payments', states=STATES, digits=(16, 2)),
|
|
|
|
'get_sum_operation')
|
|
|
|
total_deductions = fields.Function(fields.Numeric(
|
|
|
|
'Total Deductions', states=STATES, digits=(16, 2)),
|
|
|
|
'get_sum_operation')
|
|
|
|
net_payment = fields.Function(fields.Numeric(
|
|
|
|
'Net Payment', states=STATES, digits=(16, 2)),
|
|
|
|
'get_net_payment')
|
|
|
|
time_contracting = fields.Integer('Time Contracting', states=STATES,
|
|
|
|
depends=['start_period', 'end_period', 'employee'])
|
|
|
|
state = fields.Selection([
|
|
|
|
('draft', 'Draft'),
|
|
|
|
('confirmed', 'Confirmed'),
|
|
|
|
('posted', 'Posted'),
|
|
|
|
('cancel', 'Cancel'),
|
|
|
|
], 'State', readonly=True)
|
|
|
|
company = fields.Many2One('company.company', 'Company', required=True,
|
|
|
|
states={
|
|
|
|
'readonly': (Eval('state') != 'draft') | Eval('lines', [0]),
|
|
|
|
},
|
|
|
|
domain=[
|
|
|
|
('id', If(Eval('context', {}).contains('company'), '=', '!='),
|
|
|
|
Eval('context', {}).get('company', 0)),
|
|
|
|
],
|
|
|
|
depends=['state'], select=True)
|
|
|
|
description = fields.Char('Description', states=STATES, select=True)
|
|
|
|
cause = fields.Char('Cause', states=STATES)
|
|
|
|
permissons = fields.Char('Permissons', states=STATES)
|
|
|
|
journal = fields.Many2One('account.journal', 'Journal', required=True,
|
|
|
|
states=STATES)
|
|
|
|
currency = fields.Many2One('currency.currency', 'Currency',
|
|
|
|
required=True, states={
|
|
|
|
'readonly': ((Eval('state') != 'draft')
|
|
|
|
| (Eval('lines', [0]) & Eval('currency'))),
|
|
|
|
},
|
|
|
|
depends=['state'])
|
|
|
|
move = fields.Many2One('account.move', 'Move', readonly=True)
|
2021-08-03 18:16:51 +02:00
|
|
|
contract = fields.Many2One('staff.contract', 'Contract',
|
2021-02-04 16:45:22 +01:00
|
|
|
states=STATES, domain=[('employee', '=', Eval('employee'))])
|
2020-04-16 00:38:42 +02:00
|
|
|
account = fields.Many2One('account.account', 'Account',
|
|
|
|
required=True, domain=[
|
2021-06-09 18:07:42 +02:00
|
|
|
('type', '!=', None),
|
2020-04-16 00:38:42 +02:00
|
|
|
])
|
|
|
|
payrolls = fields.Function(fields.Many2Many('staff.payroll',
|
|
|
|
None, None, 'Payroll', depends=['start_period', 'end_period'],
|
|
|
|
domain=[
|
|
|
|
('employee', '=', Eval('employee')),
|
|
|
|
('kind', '=', 'normal'),
|
|
|
|
],), 'get_payrolls')
|
|
|
|
start = fields.Function(fields.Date('Start Date'), 'get_dates')
|
|
|
|
end = fields.Function(fields.Date('End Date'), 'get_dates')
|
2021-02-04 16:45:22 +01:00
|
|
|
party_to_pay = fields.Many2One('party.party', 'Party to Pay', states=STATES)
|
2021-07-21 00:21:59 +02:00
|
|
|
last_salary = fields.Numeric('Last Salary', states=STATES,
|
|
|
|
digits=(16, 2))
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(Liquidation, cls).__setup__()
|
|
|
|
cls._transitions |= set((
|
2021-02-04 16:45:22 +01:00
|
|
|
('draft', 'cancel'),
|
|
|
|
('cancel', 'draft'),
|
|
|
|
('confirmed', 'draft'),
|
|
|
|
('confirmed', 'posted'),
|
|
|
|
('draft', 'confirmed'),
|
|
|
|
('posted', 'draft'),
|
|
|
|
))
|
2020-04-16 00:38:42 +02:00
|
|
|
cls._buttons.update({
|
2020-11-04 04:34:32 +01:00
|
|
|
'draft': {
|
|
|
|
'invisible': Eval('state') == 'draft',
|
|
|
|
},
|
|
|
|
'confirm': {
|
|
|
|
'invisible': Eval('state') != 'draft',
|
|
|
|
},
|
|
|
|
'cancel': {
|
|
|
|
'invisible': Eval('state') != 'draft',
|
|
|
|
},
|
|
|
|
'post': {
|
|
|
|
'invisible': Eval('state') != 'confirmed',
|
|
|
|
},
|
|
|
|
'compute_liquidation': {
|
|
|
|
'invisible': Bool(Eval('lines')),
|
|
|
|
},
|
|
|
|
})
|
2020-04-16 00:38:42 +02:00
|
|
|
|
2022-01-19 20:50:37 +01:00
|
|
|
@classmethod
|
|
|
|
def __register__(cls, module_name):
|
|
|
|
super(Liquidation, cls).__register__(module_name)
|
|
|
|
cursor = Transaction().connection.cursor()
|
|
|
|
sql_table = cls.__table__()
|
|
|
|
|
|
|
|
cursor.execute(*sql_table.update(
|
|
|
|
[sql_table.kind], ['holidays'],
|
|
|
|
where=sql_table.kind == 'vacation'))
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_company():
|
|
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_kind():
|
|
|
|
return 'contract'
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_state():
|
|
|
|
return 'draft'
|
|
|
|
|
2022-08-04 19:50:47 +02:00
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
@staticmethod
|
|
|
|
def default_currency():
|
|
|
|
Company = Pool().get('company.company')
|
|
|
|
company = Transaction().context.get('company')
|
|
|
|
if company:
|
|
|
|
company = Company(company)
|
|
|
|
return company.currency.id
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_journal():
|
|
|
|
Configuration = Pool().get('staff.configuration')
|
|
|
|
configuration = Configuration(1)
|
|
|
|
if configuration.default_journal:
|
|
|
|
return configuration.default_journal.id
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def search_rec_name(cls, name, clause):
|
|
|
|
if clause[1].startswith('!') or clause[1].startswith('not '):
|
|
|
|
bool_op = 'AND'
|
|
|
|
else:
|
|
|
|
bool_op = 'OR'
|
2020-11-04 02:24:58 +01:00
|
|
|
return [
|
|
|
|
bool_op,
|
|
|
|
('employee',) + tuple(clause[1:]),
|
|
|
|
('number',) + tuple(clause[1:]),
|
2020-04-16 00:38:42 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
@Workflow.transition('confirmed')
|
|
|
|
def confirm(cls, records):
|
|
|
|
for rec in records:
|
2021-08-03 18:16:51 +02:00
|
|
|
if not rec.contract:
|
|
|
|
raise LiquidationEmployeeError(gettext('staff_payroll_co.msg_dont_contract'))
|
2020-04-16 00:38:42 +02:00
|
|
|
rec.set_number()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
@Workflow.transition('draft')
|
|
|
|
def draft(cls, records):
|
|
|
|
pass
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
@Workflow.transition('cancel')
|
|
|
|
def cancel(cls, records):
|
|
|
|
pass
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
@Workflow.transition('posted')
|
|
|
|
def post(cls, records):
|
|
|
|
for rec in records:
|
|
|
|
rec.create_move()
|
|
|
|
|
2020-10-17 19:00:26 +02:00
|
|
|
@classmethod
|
2020-11-27 06:34:19 +01:00
|
|
|
def create_withholding(cls, liquidation):
|
2020-10-17 19:00:26 +02:00
|
|
|
rec = liquidation
|
|
|
|
pool = Pool()
|
|
|
|
UvtWithholding = pool.get('staff.payroll.uvt_withholding')
|
|
|
|
WageType = pool.get('staff.wage_type')
|
|
|
|
wage_tax = WageType.search([('type_concept', '=', 'tax')])
|
2020-11-27 06:34:19 +01:00
|
|
|
if not wage_tax:
|
|
|
|
return
|
2020-10-17 19:00:26 +02:00
|
|
|
|
2020-11-27 06:34:19 +01:00
|
|
|
wage_tax = wage_tax[0]
|
|
|
|
deductions_month = sum([
|
|
|
|
l.amount for l in rec.lines if l.wage.definition != 'payment'
|
|
|
|
])
|
|
|
|
salary_full = rec.net_payment
|
|
|
|
payrolls = {p.end: p for p in rec.payrolls}
|
2020-12-03 22:18:04 +01:00
|
|
|
if not payrolls:
|
|
|
|
return
|
2020-11-27 06:34:19 +01:00
|
|
|
max_date = max(payrolls.keys())
|
|
|
|
if rec.liquidation_date.month == max_date.month:
|
|
|
|
payroll = payrolls[max_date]
|
|
|
|
line_tax = None
|
|
|
|
for line in payroll.lines:
|
|
|
|
if line.wage_type.type_concept == 'tax' and line.amount:
|
|
|
|
line_tax = line
|
|
|
|
if not line_tax:
|
|
|
|
deductions_month += payroll.get_deductions_month()
|
|
|
|
salary_args = payroll.get_salary_full(wage_tax)
|
|
|
|
salary_full += salary_args['salary']
|
|
|
|
|
|
|
|
base_salary_withholding = salary_full - deductions_month
|
|
|
|
amount_tax = UvtWithholding.compute_withholding(
|
|
|
|
base_salary_withholding)
|
|
|
|
amount_tax = rec.currency.round(Decimal(amount_tax))
|
|
|
|
if amount_tax:
|
|
|
|
create_tax = {
|
|
|
|
'sequence': wage_tax.sequence,
|
|
|
|
'wage': wage_tax.id,
|
|
|
|
'description': wage_tax.name,
|
|
|
|
'amount': amount_tax * -1,
|
|
|
|
'days': rec.time_contracting,
|
|
|
|
'account': wage_tax.credit_account,
|
|
|
|
}
|
|
|
|
cls.write([rec], {
|
|
|
|
'lines': [('create', [create_tax])]
|
2021-02-04 16:45:22 +01:00
|
|
|
})
|
2020-10-17 19:00:26 +02:00
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
@classmethod
|
|
|
|
@ModelView.button
|
|
|
|
def compute_liquidation(cls, records):
|
|
|
|
for rec in records:
|
|
|
|
rec.set_liquidation_lines()
|
2020-11-27 06:34:19 +01:00
|
|
|
cls.create_withholding(rec)
|
2020-04-16 00:38:42 +02:00
|
|
|
|
2022-05-03 00:46:19 +02:00
|
|
|
@classmethod
|
|
|
|
def copy(cls, records, default=None):
|
|
|
|
raise RecordDuplicateError(gettext('staff_payroll_co.msg_cannot_duplicate_record'))
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
def get_dates(self, name):
|
2021-10-27 18:49:32 +02:00
|
|
|
res = None
|
|
|
|
if self.contract:
|
|
|
|
if name == 'start':
|
|
|
|
values = [self.start_period.start]
|
|
|
|
if self.contract.start_date:
|
|
|
|
values.append(self.contract.start_date)
|
|
|
|
res = max(values)
|
|
|
|
elif name == 'end':
|
|
|
|
values = [self.end_period.end]
|
|
|
|
if self.contract.end_date:
|
|
|
|
values.append(self.contract.end_date)
|
|
|
|
res = min(values)
|
2020-04-16 00:38:42 +02:00
|
|
|
return res
|
|
|
|
|
|
|
|
def get_payrolls(self, name):
|
|
|
|
if not self.employee or not self.contract:
|
|
|
|
return
|
|
|
|
Payroll = Pool().get('staff.payroll')
|
2020-12-08 12:45:42 +01:00
|
|
|
date_start, date_end = self._get_dates()
|
2020-04-16 00:38:42 +02:00
|
|
|
payrolls = Payroll.search([
|
|
|
|
('employee', '=', self.employee.id),
|
|
|
|
('start', '>=', date_start),
|
|
|
|
('end', '<=', date_end),
|
|
|
|
('contract', '=', self.contract.id),
|
|
|
|
])
|
|
|
|
|
|
|
|
payrolls_ids = [payroll.id for payroll in payrolls]
|
|
|
|
return payrolls_ids
|
|
|
|
|
2021-07-21 00:21:59 +02:00
|
|
|
def get_salary(self, name):
|
|
|
|
res = 0
|
|
|
|
if self.contract:
|
|
|
|
if name == 'last_salary':
|
|
|
|
res = self.contract.salary
|
|
|
|
elif name == 'salary_average':
|
|
|
|
Payroll = Pool().get('staff.payroll')
|
|
|
|
Wage = Pool().get('staff.wage_type')
|
|
|
|
wage, = Wage.search([('type_concept', '=', 'unemployment')])
|
|
|
|
res = Payroll.get_salary_average(self.end, self.employee, self.contract, wage)
|
|
|
|
values = [self.end_period.end]
|
2021-08-04 22:46:00 +02:00
|
|
|
if self.contract.end_date:
|
|
|
|
values.append(self.contract.end_date)
|
2021-07-21 00:21:59 +02:00
|
|
|
res = min(values)
|
|
|
|
|
|
|
|
return res
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
def create_move(self):
|
|
|
|
pool = Pool()
|
|
|
|
Move = pool.get('account.move')
|
|
|
|
MoveLine = pool.get('account.move.line')
|
|
|
|
Period = pool.get('account.period')
|
|
|
|
if self.move:
|
|
|
|
return
|
|
|
|
|
|
|
|
move_lines, grouped = self.get_moves_lines()
|
|
|
|
if move_lines:
|
|
|
|
period_id = Period.find(self.company.id, date=self.liquidation_date)
|
|
|
|
move, = Move.create([{
|
|
|
|
'journal': self.journal.id,
|
2022-05-03 15:38:28 +02:00
|
|
|
'origin': str(self),
|
2020-04-16 00:38:42 +02:00
|
|
|
'period': period_id,
|
|
|
|
'date': self.liquidation_date,
|
|
|
|
'description': self.description,
|
|
|
|
'lines': [('create', move_lines)],
|
|
|
|
}])
|
|
|
|
self.write([self], {'move': move.id})
|
|
|
|
for ml in move.lines:
|
2020-11-04 02:24:58 +01:00
|
|
|
if ml.account.id not in grouped.keys() or (
|
2021-06-09 18:07:42 +02:00
|
|
|
ml.account.type.statement not in ('balance')):
|
2020-04-16 00:38:42 +02:00
|
|
|
continue
|
|
|
|
to_reconcile = [ml]
|
|
|
|
to_reconcile.extend(grouped[ml.account.id]['lines'])
|
2021-08-06 16:42:00 +02:00
|
|
|
if len(to_reconcile) > 1:
|
|
|
|
MoveLine.reconcile(set(to_reconcile))
|
2020-06-12 07:07:52 +02:00
|
|
|
Move.post([move])
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
def get_moves_lines(self):
|
|
|
|
lines_moves = []
|
|
|
|
to_reconcile = []
|
|
|
|
grouped = {}
|
|
|
|
amount = []
|
|
|
|
for line in self.lines:
|
|
|
|
if line.move_lines:
|
|
|
|
for moveline in line.move_lines:
|
|
|
|
to_reconcile.append(moveline)
|
2022-12-22 15:36:06 +01:00
|
|
|
account_id = moveline.account.id
|
|
|
|
amount_line = abs(moveline.debit - moveline.credit)
|
|
|
|
if account_id not in grouped.keys():
|
|
|
|
grouped[account_id] = {
|
2020-04-16 00:38:42 +02:00
|
|
|
'amount': [],
|
|
|
|
'description': line.description,
|
|
|
|
'lines': [],
|
|
|
|
}
|
2022-12-22 15:36:06 +01:00
|
|
|
grouped[account_id]['amount'].append(amount_line)
|
|
|
|
grouped[account_id]['lines'].append(moveline)
|
|
|
|
amount.append(amount_line)
|
|
|
|
elif line.wage.definition == 'discount':
|
|
|
|
account_id = line.account.id
|
|
|
|
if account_id not in grouped.keys():
|
|
|
|
grouped[account_id] = {
|
|
|
|
'amount': [],
|
|
|
|
'description': line.description,
|
|
|
|
'lines': [],
|
|
|
|
}
|
|
|
|
grouped[account_id]['amount'].append(line.amount)
|
|
|
|
amount.append(line.amount)
|
2020-04-21 16:03:35 +02:00
|
|
|
|
|
|
|
for adjust in line.adjustments:
|
2022-08-02 16:48:40 +02:00
|
|
|
key = adjust.account.id
|
|
|
|
if key not in grouped.keys():
|
2022-12-22 15:36:06 +01:00
|
|
|
grouped[key] = {
|
2020-04-21 16:03:35 +02:00
|
|
|
'amount': [],
|
|
|
|
'description': adjust.description,
|
2020-04-16 00:38:42 +02:00
|
|
|
'lines': [],
|
|
|
|
}
|
2022-08-02 16:48:40 +02:00
|
|
|
if hasattr(adjust, 'analytic_account') and adjust.analytic_account:
|
2022-12-22 15:36:06 +01:00
|
|
|
grouped[key]['analytic'] = adjust.analytic_account
|
2020-04-21 16:03:35 +02:00
|
|
|
grouped[adjust.account.id]['amount'].append(adjust.amount)
|
|
|
|
amount.append(adjust.amount)
|
2022-08-02 16:48:40 +02:00
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
for account_id, values in grouped.items():
|
2020-12-03 19:58:38 +01:00
|
|
|
_amount = sum(values['amount'])
|
|
|
|
debit = _amount
|
|
|
|
credit = _ZERO
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
lines_moves.append(self._prepare_line(values['description'],
|
2022-08-02 16:48:40 +02:00
|
|
|
account_id, debit=debit, credit=credit, analytic=values.get('analytic', None)))
|
2020-04-16 00:38:42 +02:00
|
|
|
if lines_moves:
|
2021-02-04 16:45:22 +01:00
|
|
|
lines_moves.append(self._prepare_line(
|
|
|
|
self.description,
|
|
|
|
self.account,
|
|
|
|
credit=sum(amount),
|
|
|
|
party_to_pay=self.party_to_pay,
|
|
|
|
))
|
2020-04-16 00:38:42 +02:00
|
|
|
return lines_moves, grouped
|
|
|
|
|
2022-08-02 16:48:40 +02:00
|
|
|
def _prepare_line(self, description, account_id, debit=_ZERO, credit=_ZERO, party_to_pay=None, analytic=None):
|
2020-04-16 00:38:42 +02:00
|
|
|
if debit < _ZERO:
|
|
|
|
credit = debit
|
|
|
|
debit = _ZERO
|
2020-12-03 19:58:38 +01:00
|
|
|
elif credit < _ZERO:
|
2020-04-16 00:38:42 +02:00
|
|
|
debit = credit
|
|
|
|
credit = _ZERO
|
|
|
|
|
|
|
|
credit = abs(credit)
|
|
|
|
debit = abs(debit)
|
2021-02-04 16:45:22 +01:00
|
|
|
party_id = self.employee.party.id
|
|
|
|
if party_to_pay:
|
|
|
|
party_id = self.party_to_pay.id
|
2020-04-16 00:38:42 +02:00
|
|
|
res = {
|
|
|
|
'description': description,
|
|
|
|
'debit': debit,
|
|
|
|
'credit': credit,
|
|
|
|
'account': account_id,
|
2021-02-04 16:45:22 +01:00
|
|
|
'party': party_id,
|
2020-04-16 00:38:42 +02:00
|
|
|
}
|
2022-08-02 16:48:40 +02:00
|
|
|
if analytic:
|
|
|
|
res['analytic_lines']= [
|
|
|
|
('create', [{
|
|
|
|
'debit': res['debit'],
|
|
|
|
'credit': res['credit'],
|
|
|
|
'account': analytic.id,
|
|
|
|
'date': self.liquidation_date
|
|
|
|
}])]
|
2020-04-16 00:38:42 +02:00
|
|
|
return res
|
|
|
|
|
2020-12-08 12:45:42 +01:00
|
|
|
def _get_dates(self):
|
2020-04-16 00:38:42 +02:00
|
|
|
date_end_contract = None
|
2021-02-04 20:15:05 +01:00
|
|
|
date_start = self.start_period.start
|
2020-04-16 00:38:42 +02:00
|
|
|
if self.contract.start_date > self.start_period.start:
|
|
|
|
date_start = self.contract.start_date
|
|
|
|
|
|
|
|
if self.contract.futhermores:
|
|
|
|
date_end_contract = self.contract.finished_date
|
|
|
|
|
2021-02-04 20:15:05 +01:00
|
|
|
date_end = self.end_period.end
|
2020-04-16 00:38:42 +02:00
|
|
|
if date_end_contract and date_end_contract <= self.end_period.end:
|
|
|
|
date_end = date_end_contract
|
|
|
|
elif self.contract.end_date and self.contract.end_date < self.end_period.end:
|
|
|
|
date_end = self.contract.end_date
|
|
|
|
return date_start, date_end
|
|
|
|
|
2021-02-18 17:51:38 +01:00
|
|
|
def _get_dates_liquidation(self):
|
|
|
|
# date_end_contract = None
|
|
|
|
date_start = self.start_period.start
|
|
|
|
if self.contract.start_date > self.start_period.start:
|
|
|
|
date_start = self.contract.start_date
|
|
|
|
|
|
|
|
# if self.contract.futhermores:
|
|
|
|
# date_end_contract = self.contract.finished_date
|
|
|
|
|
|
|
|
date_end = self.end_period.end
|
|
|
|
# if date_end_contract and date_end_contract <= self.end_period.end:
|
|
|
|
# date_end = date_end_contract
|
|
|
|
# elif self.contract.end_date and self.contract.end_date < self.end_period.end:
|
|
|
|
# date_end = self.contract.end_date
|
|
|
|
return date_start, date_end
|
|
|
|
|
2021-02-07 17:24:50 +01:00
|
|
|
@classmethod
|
2021-02-09 22:57:29 +01:00
|
|
|
def get_moves_lines_pending(cls, employee, wage_type, effective_date):
|
2021-02-07 17:24:50 +01:00
|
|
|
MoveLine = Pool().get('account.move.line')
|
|
|
|
lines = []
|
|
|
|
if not wage_type.credit_account:
|
|
|
|
return
|
|
|
|
|
|
|
|
account_id = wage_type.credit_account.id
|
|
|
|
lines = MoveLine.search([
|
2021-02-09 22:57:29 +01:00
|
|
|
('move.date', '<=', effective_date),
|
|
|
|
('credit', '>', 0),
|
2021-02-07 17:24:50 +01:00
|
|
|
('account', '=', account_id),
|
|
|
|
('party', '=', employee.party.id),
|
|
|
|
('reconciliation', '=', None),
|
|
|
|
])
|
|
|
|
return lines
|
|
|
|
|
2022-05-03 00:28:58 +02:00
|
|
|
@classmethod
|
|
|
|
def delete(cls, records):
|
|
|
|
# Cancel before delete
|
|
|
|
cls.cancel(records)
|
|
|
|
for liquidation in records:
|
|
|
|
if liquidation.state != 'cancel':
|
|
|
|
raise LiquidationDeleteError(
|
|
|
|
gettext('staff_payroll_co.msg_delete_cancel', liquidation=liquidation.rec_name))
|
|
|
|
if liquidation.move:
|
|
|
|
raise LiquidationDeleteError(
|
|
|
|
gettext('staff_payroll_co.msg_existing_move', liquidation=liquidation.rec_name))
|
2022-06-02 16:48:12 +02:00
|
|
|
super(Liquidation, cls).delete(records)
|
2022-05-03 00:28:58 +02:00
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
def set_liquidation_lines(self):
|
|
|
|
pool = Pool()
|
|
|
|
Payroll = pool.get('staff.payroll')
|
2022-06-03 19:13:50 +02:00
|
|
|
LiquidationMove = pool.get('staff.liquidation.line-move.line')
|
2021-02-18 17:51:38 +01:00
|
|
|
date_start, date_end = self._get_dates_liquidation()
|
2020-04-16 00:38:42 +02:00
|
|
|
payrolls = Payroll.search([
|
|
|
|
('employee', '=', self.employee.id),
|
|
|
|
('start', '>=', date_start),
|
|
|
|
('end', '<=', date_end),
|
|
|
|
('contract', '=', self.contract.id),
|
2021-08-06 16:42:00 +02:00
|
|
|
('state', '=', 'posted')
|
2020-04-16 00:38:42 +02:00
|
|
|
])
|
|
|
|
wages = {}
|
2021-02-07 17:24:50 +01:00
|
|
|
wages_target = {}
|
2020-04-16 00:38:42 +02:00
|
|
|
for payroll in payrolls:
|
|
|
|
for l in payroll.lines:
|
|
|
|
if not l.wage_type.contract_finish:
|
|
|
|
continue
|
|
|
|
if self.kind == 'contract':
|
|
|
|
if l.wage_type.type_concept not in CONTRACT:
|
|
|
|
continue
|
2022-01-19 20:29:53 +01:00
|
|
|
elif self.kind != l.wage_type.type_concept:
|
2020-04-16 00:38:42 +02:00
|
|
|
continue
|
2020-07-16 00:14:01 +02:00
|
|
|
|
2021-02-07 17:24:50 +01:00
|
|
|
if l.wage_type.id not in wages_target.keys():
|
|
|
|
mlines = self.get_moves_lines_pending(
|
2021-02-09 22:57:29 +01:00
|
|
|
payroll.employee, l.wage_type, date_end
|
2021-02-07 17:24:50 +01:00
|
|
|
)
|
|
|
|
if not mlines:
|
|
|
|
continue
|
|
|
|
wages_target[l.wage_type.id] = [
|
|
|
|
l.wage_type.credit_account.id,
|
|
|
|
mlines,
|
|
|
|
l.wage_type,
|
|
|
|
]
|
|
|
|
|
|
|
|
# wages.append(l.wage_type.id)
|
2020-07-16 00:14:01 +02:00
|
|
|
# This looks for lines provisioned before start period
|
2020-08-31 18:03:14 +02:00
|
|
|
# old_lines_provisioned = MoveLine.search([
|
|
|
|
# ('party', '=', self.employee.party.id),
|
|
|
|
# ('move.date', '<', date_start),
|
|
|
|
# ('reconciliation', '=', None),
|
|
|
|
# ('account', '=', account_id),
|
|
|
|
# ])
|
|
|
|
# lines.extend(old_lines_provisioned)
|
2021-02-07 17:24:50 +01:00
|
|
|
for (account_id, lines, wage_type) in wages_target.values():
|
|
|
|
values = []
|
|
|
|
lines_to_reconcile = []
|
|
|
|
for line in lines:
|
|
|
|
values.append(abs(line.debit - line.credit))
|
|
|
|
lines_to_reconcile.append(line.id)
|
2022-01-25 20:08:32 +01:00
|
|
|
value = self.get_line_(wage_type, sum(values), self.time_contracting, account_id, party=self.party_to_pay)
|
2022-06-03 19:13:50 +02:00
|
|
|
lines = LiquidationMove.search([
|
|
|
|
('move_line', 'in', lines_to_reconcile)
|
|
|
|
])
|
|
|
|
if lines:
|
|
|
|
liquidation = lines[0].line.liquidation
|
|
|
|
raise RecordDuplicateError(
|
|
|
|
gettext('staff_payroll_co.msg_duplicate_liquidation',
|
|
|
|
liquidation=liquidation.id, state=liquidation.state))
|
2022-01-25 20:08:32 +01:00
|
|
|
value.update({
|
2021-02-07 17:24:50 +01:00
|
|
|
'move_lines': [('add', lines_to_reconcile)],
|
2022-01-25 20:08:32 +01:00
|
|
|
})
|
|
|
|
wages[wage_type.id] = value
|
2020-04-16 00:38:42 +02:00
|
|
|
|
2022-08-02 16:48:40 +02:00
|
|
|
self.write([self], {'lines': [('create', wages.values())]})
|
2022-01-25 20:08:32 +01:00
|
|
|
if self.kind == 'contract':
|
|
|
|
self.process_loans_to_pay()
|
|
|
|
|
|
|
|
def get_line_(self, wage, amount, days, account_id, party=None):
|
|
|
|
value = {
|
|
|
|
'sequence': wage.sequence,
|
|
|
|
'wage': wage.id,
|
|
|
|
'account': account_id,
|
|
|
|
'description': wage.name,
|
|
|
|
'amount': amount,
|
|
|
|
'days': days,
|
|
|
|
'party_to_pay': party,
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
|
|
|
|
def process_loans_to_pay(self):
|
|
|
|
pool = Pool()
|
|
|
|
MoveLine = pool.get('account.move.line')
|
|
|
|
LoanLine = pool.get('staff.loan.line')
|
|
|
|
LiquidationLine = pool.get('staff.liquidation.line')
|
|
|
|
|
2022-12-21 23:59:51 +01:00
|
|
|
# for wage in self.employee.mandatory_wages:
|
|
|
|
# if wage.wage_type.type_concept == 'loan':
|
|
|
|
# wage_type = wage.wage_type
|
|
|
|
# account_id = wage_type.credit_account.id
|
|
|
|
dom = [
|
|
|
|
('loan.wage_type', '!=', None),
|
|
|
|
('loan.party', '=', self.employee.party.id),
|
|
|
|
('state', 'in', ['pending', 'partial']),
|
|
|
|
]
|
|
|
|
|
|
|
|
lines_loan = LoanLine.search(dom)
|
|
|
|
for m in lines_loan:
|
|
|
|
|
|
|
|
move_lines = MoveLine.search([
|
|
|
|
('origin', 'in', ['staff.loan.line,' + str(m)]),
|
|
|
|
])
|
|
|
|
party = m.loan.party_to_pay.id if m.loan.party_to_pay else None
|
2022-12-22 15:36:06 +01:00
|
|
|
res = self.get_line_(m.wage_type, m.amount, 1, m.credit_account.id, party=party)
|
2022-12-21 23:59:51 +01:00
|
|
|
res['move_lines'] = [('add', move_lines)]
|
|
|
|
res['liquidation'] = self.id
|
|
|
|
line_, = LiquidationLine.create([res])
|
|
|
|
LoanLine.write([m], {'state': 'paid', 'origin': line_})
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
@fields.depends('start_period', 'end_period', 'contract')
|
|
|
|
def on_change_with_time_contracting(self):
|
|
|
|
delta = None
|
|
|
|
if self.start_period and self.end_period and self.contract:
|
2021-02-04 17:35:52 +01:00
|
|
|
try:
|
|
|
|
date_start, date_end = self._get_dates()
|
2021-12-16 15:03:03 +01:00
|
|
|
delta = self.contract.get_time_days(date_start, date_end)
|
2021-02-04 17:35:52 +01:00
|
|
|
except:
|
2021-06-09 18:07:42 +02:00
|
|
|
raise LiquidationEmployeeError(
|
|
|
|
gettext('staff_payroll_co.msg_error_dates', s=self.employee.party.name))
|
2021-02-04 17:58:02 +01:00
|
|
|
delta = 0
|
2020-04-16 00:38:42 +02:00
|
|
|
return delta
|
|
|
|
|
2021-07-21 00:21:59 +02:00
|
|
|
def on_change_contract(self):
|
|
|
|
if self.contract:
|
|
|
|
self.last_salary = self.contract.salary
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
def set_number(self):
|
|
|
|
if self.number:
|
|
|
|
return
|
|
|
|
pool = Pool()
|
|
|
|
Configuration = pool.get('staff.configuration')
|
|
|
|
configuration = Configuration(1)
|
|
|
|
if not configuration.staff_liquidation_sequence:
|
2021-06-09 18:07:42 +02:00
|
|
|
raise MissingSecuenceLiquidation(
|
|
|
|
gettext('staff_payroll_co.msg_sequence_missing'))
|
|
|
|
seq = configuration.staff_liquidation_sequence.get()
|
|
|
|
self.write([self], {'number': seq})
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
def get_sum_operation(self, name):
|
|
|
|
res = []
|
|
|
|
for line in self.lines:
|
|
|
|
if not line.amount:
|
|
|
|
continue
|
|
|
|
if name == 'gross_payments' and line.wage.definition == 'payment':
|
|
|
|
res.append(line.amount)
|
|
|
|
elif name == 'total_deductions' and line.wage.definition != 'payment':
|
|
|
|
res.append(line.amount)
|
|
|
|
return sum(res)
|
|
|
|
|
|
|
|
def get_net_payment(self, name):
|
2021-08-06 16:42:00 +02:00
|
|
|
res = (self.gross_payments or 0) - abs((self.total_deductions or 0))
|
2021-02-04 17:26:53 +01:00
|
|
|
if res:
|
|
|
|
return self.currency.round(res)
|
|
|
|
return 0
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
def get_currency(self, name):
|
|
|
|
return self.company.currency.id
|
|
|
|
|
|
|
|
|
|
|
|
class LiquidationLine(ModelSQL, ModelView):
|
|
|
|
'Staff Liquidation Line'
|
|
|
|
__name__ = 'staff.liquidation.line'
|
|
|
|
sequence = fields.Integer('Sequence', required=True)
|
|
|
|
liquidation = fields.Many2One('staff.liquidation', 'Liquidation',
|
2020-11-04 02:24:58 +01:00
|
|
|
required=True)
|
2020-04-16 00:38:42 +02:00
|
|
|
wage = fields.Many2One('staff.wage_type', 'Wage Type', required=True)
|
|
|
|
description = fields.Char('Description', required=True)
|
2020-12-08 12:45:42 +01:00
|
|
|
amount = fields.Numeric('Amount', digits=(16, 2), required=True,
|
|
|
|
depends=['adjustments', 'move_lines'])
|
2020-04-16 00:38:42 +02:00
|
|
|
days = fields.Integer('Days')
|
|
|
|
notes = fields.Char('Notes')
|
|
|
|
account = fields.Many2One('account.account', 'Account')
|
|
|
|
move_lines = fields.Many2Many('staff.liquidation.line-move.line',
|
|
|
|
'line', 'move_line', 'Liquidation Line - Move Line',
|
2020-11-04 02:24:58 +01:00
|
|
|
domain=[
|
2020-04-16 00:38:42 +02:00
|
|
|
('party', '=', Eval('party')),
|
|
|
|
('account', '=', Eval('account')),
|
|
|
|
], depends=['party', 'account'])
|
|
|
|
party = fields.Function(fields.Many2One('party.party', 'Party'),
|
|
|
|
'get_party')
|
2020-12-08 12:45:42 +01:00
|
|
|
adjustments = fields.One2Many('staff.liquidation.line_adjustment',
|
|
|
|
'staff_liquidation_line', 'Adjustments')
|
2021-02-04 16:45:22 +01:00
|
|
|
party_to_pay = fields.Many2One('party.party', 'Party to Pay')
|
2021-07-21 00:21:59 +02:00
|
|
|
salary_average = fields.Function(fields.Numeric('Salary Average',
|
|
|
|
digits=(16, 2)), 'get_average_payroll')
|
2020-04-18 20:23:40 +02:00
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(LiquidationLine, cls).__setup__()
|
|
|
|
cls._order.insert(0, ('sequence', 'ASC'))
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def __register__(cls, module_name):
|
2021-06-09 18:07:42 +02:00
|
|
|
table_h = cls.__table_handler__(module_name)
|
2020-04-16 00:38:42 +02:00
|
|
|
|
|
|
|
# Migration from 4.0: remove hoard_amount
|
2021-06-09 18:07:42 +02:00
|
|
|
if table_h.column_exist('hoard_amount'):
|
|
|
|
table_h.drop_column('hoard_amount')
|
2020-04-16 00:38:42 +02:00
|
|
|
super(LiquidationLine, cls).__register__(module_name)
|
|
|
|
|
|
|
|
def get_party(self, name=None):
|
|
|
|
if self.liquidation.employee:
|
|
|
|
return self.liquidation.employee.party.id
|
|
|
|
|
|
|
|
@fields.depends('wage', 'description', 'amount', 'liquidation',
|
2020-11-04 02:24:58 +01:00
|
|
|
'_parent_liquidation.employee', '_parent_liquidation.time_contracting',
|
|
|
|
'_parent_liquidation.start_period', '_parent_liquidation.end_period',
|
|
|
|
'_parent_liquidation.currency', 'sequence')
|
2020-04-16 00:38:42 +02:00
|
|
|
def on_change_wage(self):
|
|
|
|
if not self.wage:
|
|
|
|
return
|
|
|
|
self.sequence = self.wage.sequence
|
|
|
|
self.description = self.wage.name
|
|
|
|
self.days = self.liquidation.time_contracting
|
2020-04-21 16:03:35 +02:00
|
|
|
|
|
|
|
@fields.depends('amount', 'adjustments', 'move_lines')
|
|
|
|
def on_change_with_amount(self, name=None):
|
|
|
|
amount_ = 0
|
|
|
|
if self.adjustments:
|
|
|
|
amount_ += sum([ad.amount or 0 for ad in self.adjustments])
|
|
|
|
if self.move_lines:
|
|
|
|
amount_ += sum([(ml.credit - ml.debit) for ml in self.move_lines])
|
|
|
|
return amount_
|
2020-04-16 00:38:42 +02:00
|
|
|
|
2021-07-21 00:21:59 +02:00
|
|
|
def get_average_payroll(self, name):
|
|
|
|
contract = self.liquidation.contract
|
|
|
|
employee = self.liquidation.employee
|
|
|
|
end = self.liquidation.end
|
|
|
|
Payroll = Pool().get('staff.payroll')
|
2021-09-13 22:13:04 +02:00
|
|
|
try:
|
|
|
|
res = Payroll.get_salary_average(end, employee, contract, self.wage) * 30
|
|
|
|
except:
|
|
|
|
res = 0
|
2021-07-21 00:21:59 +02:00
|
|
|
return res
|
|
|
|
|
2022-01-25 22:59:26 +01:00
|
|
|
@classmethod
|
|
|
|
def delete(cls, lines):
|
|
|
|
LoanLine = Pool().get('staff.loan.line')
|
|
|
|
loan_lines = LoanLine.search([
|
|
|
|
('origin', 'in', ['staff.liquidation.line,' + str(l.id) for l in lines])
|
|
|
|
])
|
|
|
|
LoanLine.write([m for m in loan_lines], {'state': 'pending', 'origin': None})
|
|
|
|
super(LiquidationLine, cls).delete(lines)
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
|
2020-04-21 16:03:35 +02:00
|
|
|
class LiquidationLineAdjustment(ModelSQL, ModelView):
|
|
|
|
'Liquidation Adjustment'
|
|
|
|
__name__ = 'staff.liquidation.line_adjustment'
|
2020-04-18 20:23:40 +02:00
|
|
|
staff_liquidation_line = fields.Many2One('staff.liquidation.line', 'Line',
|
|
|
|
required=True, select=True)
|
|
|
|
account = fields.Many2One('account.account', 'Acount',
|
|
|
|
required=True, domain=[
|
|
|
|
('company', '=', Eval('context', {}).get('company', -1)),
|
2021-06-09 18:07:42 +02:00
|
|
|
('type', '!=', None),
|
2020-04-18 20:23:40 +02:00
|
|
|
])
|
|
|
|
description = fields.Char('Description', required=True)
|
|
|
|
amount = fields.Numeric('Amount', digits=(10,2), required=True)
|
|
|
|
|
|
|
|
|
2020-04-16 00:38:42 +02:00
|
|
|
class LiquidationReport(Report):
|
|
|
|
__name__ = 'staff.liquidation.report'
|
|
|
|
|
|
|
|
|
|
|
|
class LiquidationLineMoveLine(ModelSQL):
|
|
|
|
"Liquidation Line - MoveLine"
|
|
|
|
__name__ = "staff.liquidation.line-move.line"
|
|
|
|
_table = 'staff_liquidation_line_move_line_rel'
|
|
|
|
line = fields.Many2One('staff.liquidation.line', 'Line',
|
|
|
|
ondelete='CASCADE', select=True, required=True)
|
|
|
|
move_line = fields.Many2One('account.move.line', 'Move Line',
|
|
|
|
ondelete='RESTRICT', select=True, required=True)
|
2020-04-18 20:23:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
class LiquidationAdjustmentStart(ModelView):
|
|
|
|
'Create Liquidation Adjustment Start'
|
|
|
|
__name__ = 'staff.liquidation_adjustment.start'
|
|
|
|
wage_type = fields.Many2One('staff.wage_type', 'Wage Type', required=True, domain=[
|
|
|
|
('contract_finish', '=', True)
|
|
|
|
])
|
|
|
|
amount = fields.Numeric('Amount', required=True)
|
|
|
|
account = fields.Many2One('account.account', 'Acount',
|
|
|
|
required=True, domain=[
|
|
|
|
('company', '=', Eval('context', {}).get('company', -1)),
|
2021-06-09 18:07:42 +02:00
|
|
|
('type', '!=', None),
|
2020-04-18 20:23:40 +02:00
|
|
|
])
|
|
|
|
description = fields.Char('Description', required=True)
|
|
|
|
|
|
|
|
@fields.depends('wage_type', 'account')
|
2020-04-21 16:03:35 +02:00
|
|
|
def on_change_wage_type(self):
|
|
|
|
if self.wage_type and self.wage_type.debit_account:
|
|
|
|
self.account = self.wage_type.debit_account.id
|
2020-04-18 20:23:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
class LiquidationAdjustment(Wizard):
|
|
|
|
'Create Liquidation Adjustment'
|
|
|
|
__name__ = 'staff.liquidation_adjustment'
|
|
|
|
start = StateView('staff.liquidation_adjustment.start',
|
|
|
|
'staff_payroll_co.liquidation_adjustment_start_view_form', [
|
|
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
|
|
Button('Create', 'accept', 'tryton-ok', default=True),
|
|
|
|
])
|
|
|
|
accept = StateTransition()
|
|
|
|
|
2020-04-21 16:03:35 +02:00
|
|
|
@classmethod
|
|
|
|
def __setup__(cls):
|
|
|
|
super(LiquidationAdjustment, cls).__setup__()
|
|
|
|
|
|
|
|
def create_adjustment(self, line):
|
|
|
|
LineAdjustment = Pool().get('staff.liquidation.line_adjustment')
|
|
|
|
adjust, = LineAdjustment.create([{
|
|
|
|
'staff_liquidation_line': line.id,
|
|
|
|
'account': self.start.account.id,
|
|
|
|
'description': self.start.description,
|
|
|
|
'amount': self.start.amount,
|
|
|
|
}])
|
|
|
|
return adjust
|
2020-04-18 20:23:40 +02:00
|
|
|
|
2020-04-21 16:03:35 +02:00
|
|
|
def transition_accept(self):
|
|
|
|
pool = Pool()
|
|
|
|
Liquidation = pool.get('staff.liquidation')
|
|
|
|
LiquidationLine = pool.get('staff.liquidation.line')
|
|
|
|
id_ = Transaction().context['active_id']
|
|
|
|
|
|
|
|
liquidation, = Liquidation.search([('id', '=', id_)])
|
|
|
|
|
|
|
|
line_created = None
|
|
|
|
if liquidation:
|
|
|
|
if liquidation.move:
|
2021-06-09 18:07:42 +02:00
|
|
|
raise LiquidationMoveError(
|
|
|
|
gettext('staff_payroll_co.msg_liquidation_with_move', s=liquidation.number))
|
2020-04-21 16:03:35 +02:00
|
|
|
for line in liquidation.lines:
|
|
|
|
if line.wage.id == self.start.wage_type.id:
|
|
|
|
if line.amount:
|
|
|
|
line.amount += self.start.amount
|
|
|
|
line.save()
|
|
|
|
line_created = self.create_adjustment(line)
|
|
|
|
if not line_created:
|
|
|
|
line, = LiquidationLine.create([{
|
2020-06-13 01:50:41 +02:00
|
|
|
'sequence': len(liquidation.lines) + 1,
|
2020-04-21 16:03:35 +02:00
|
|
|
'liquidation': liquidation.id,
|
|
|
|
'wage': self.start.wage_type.id,
|
|
|
|
'description': self.start.wage_type.name,
|
|
|
|
'amount': self.start.amount,
|
|
|
|
}])
|
|
|
|
self.create_adjustment(line)
|
2020-04-18 20:23:40 +02:00
|
|
|
|
|
|
|
return 'end'
|
2020-06-10 22:59:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
class LiquidationGroupStart(ModelView):
|
|
|
|
'Liquidation Group Start'
|
|
|
|
__name__ = 'staff.liquidation_group.start'
|
|
|
|
start_period = fields.Many2One('staff.payroll.period', 'Start Period',
|
2021-02-04 20:15:05 +01:00
|
|
|
required=True)
|
2020-06-10 22:59:25 +02:00
|
|
|
end_period = fields.Many2One('staff.payroll.period', 'End Period',
|
2021-02-04 20:15:05 +01:00
|
|
|
required=True, depends=['start_period'])
|
2020-06-10 22:59:25 +02:00
|
|
|
kind = fields.Selection([
|
|
|
|
('contract', 'Contract'),
|
|
|
|
('bonus_service', 'Bonus Service'),
|
|
|
|
('interest', 'Interest'),
|
2022-03-10 17:56:24 +01:00
|
|
|
('holidays', 'Vacation'),
|
2021-02-04 16:45:22 +01:00
|
|
|
('unemployment', 'Unemployment'),
|
2020-06-10 22:59:25 +02:00
|
|
|
], 'Kind', required=True)
|
|
|
|
liquidation_date = fields.Date('Liquidation Date', required=True)
|
|
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
|
|
description = fields.Char('Description', required=True)
|
2020-12-03 22:04:55 +01:00
|
|
|
account = fields.Many2One('account.account', 'Account', required=True,
|
|
|
|
domain=[
|
2021-06-09 18:07:42 +02:00
|
|
|
('type.payable', '=', True),
|
2020-06-10 22:59:25 +02:00
|
|
|
('company', '=', Eval('company'))
|
|
|
|
])
|
2021-06-05 01:15:33 +02:00
|
|
|
employees = fields.Many2Many('company.employee', None, None, 'Employee')
|
2021-02-04 16:45:22 +01:00
|
|
|
party_to_pay = fields.Many2One('party.party', 'Party to Pay', states={
|
|
|
|
'invisible': Eval('kind') != 'unemployment'
|
|
|
|
})
|
2020-06-10 22:59:25 +02:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_company():
|
|
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
|
|
|
|
|
|
class LiquidationGroup(Wizard):
|
|
|
|
'Liquidation Group'
|
|
|
|
__name__ = 'staff.liquidation_group'
|
|
|
|
start = StateView('staff.liquidation_group.start',
|
2020-11-04 02:24:58 +01:00
|
|
|
'staff_payroll_co.liquidation_group_start_view_form', [
|
2020-06-13 01:50:41 +02:00
|
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
|
|
Button('Accept', 'open_', 'tryton-ok', default=True),
|
2020-11-04 02:24:58 +01:00
|
|
|
])
|
2020-06-10 22:59:25 +02:00
|
|
|
open_ = StateTransition()
|
|
|
|
|
|
|
|
def transition_open_(self):
|
|
|
|
pool = Pool()
|
|
|
|
Liquidation = pool.get('staff.liquidation')
|
2020-06-12 03:55:45 +02:00
|
|
|
Contract = pool.get('staff.contract')
|
2021-02-04 16:45:22 +01:00
|
|
|
Employee = pool.get('company.employee')
|
2021-06-05 00:49:33 +02:00
|
|
|
Period = pool.get('staff.payroll.period')
|
2021-02-04 20:15:05 +01:00
|
|
|
to_liquidation = []
|
2020-06-10 22:59:25 +02:00
|
|
|
|
|
|
|
start_period = self.start.start_period.id
|
|
|
|
end_period = self.start.end_period.id
|
|
|
|
kind = self.start.kind
|
|
|
|
liquidation_date = self.start.liquidation_date
|
|
|
|
company_id = self.start.company.id
|
|
|
|
description = self.start.description
|
|
|
|
currency_id = self.start.company.currency.id
|
|
|
|
account_id = self.start.account.id
|
|
|
|
|
2021-06-05 00:49:33 +02:00
|
|
|
start_date = self.start.start_period.start
|
|
|
|
end_date = self.start.end_period.end
|
|
|
|
|
|
|
|
periods = Period.search([
|
|
|
|
('start', '>=', start_date),
|
|
|
|
('start', '<=', end_date)
|
|
|
|
])
|
|
|
|
|
2020-12-03 22:04:55 +01:00
|
|
|
dom_contracts = [('kind', '=', kind)]
|
|
|
|
if self.start.employees:
|
|
|
|
employees = self.start.employees
|
|
|
|
employee_ids = [e.id for e in employees]
|
|
|
|
dom_contracts.append(('employee', 'in', employee_ids))
|
2021-02-04 16:45:22 +01:00
|
|
|
else:
|
2021-06-05 01:07:58 +02:00
|
|
|
employees = Employee.search([('contract', '!=', None)])
|
2020-12-03 22:04:55 +01:00
|
|
|
|
2021-06-05 00:49:33 +02:00
|
|
|
if periods:
|
|
|
|
periods_ids = [p.id for p in periods]
|
|
|
|
dom_contracts.append(('start_period', 'in', periods_ids))
|
|
|
|
dom_contracts.append(('end_period', 'in', periods_ids))
|
|
|
|
|
2020-12-03 22:04:55 +01:00
|
|
|
contracts = Liquidation.search_read(
|
|
|
|
dom_contracts, fields_names=['contract']
|
|
|
|
)
|
2020-06-10 22:59:25 +02:00
|
|
|
contract_ids = [i['contract'] for i in contracts]
|
|
|
|
for employee in employees:
|
2020-06-12 04:00:17 +02:00
|
|
|
contracts = Contract.search([
|
|
|
|
('employee', '=', employee.id),
|
|
|
|
('id', 'not in', contract_ids),
|
2021-06-05 01:07:58 +02:00
|
|
|
('id', 'not in', contract_ids),
|
2020-06-12 06:41:33 +02:00
|
|
|
])
|
2020-12-03 22:04:55 +01:00
|
|
|
if not contracts:
|
|
|
|
continue
|
|
|
|
|
|
|
|
contract = contracts[0]
|
2021-02-07 17:24:50 +01:00
|
|
|
|
|
|
|
if kind == 'unemployment':
|
|
|
|
wages = [
|
|
|
|
mw for mw in employee.mandatory_wages
|
|
|
|
if mw.wage_type.type_concept == 'unemployment'
|
|
|
|
]
|
|
|
|
if not wages:
|
|
|
|
continue
|
|
|
|
wage = wages[0]
|
|
|
|
lines = Liquidation.get_moves_lines_pending(
|
2021-02-09 22:57:29 +01:00
|
|
|
employee, wage.wage_type, self.start.end_period.end
|
2021-02-07 17:24:50 +01:00
|
|
|
)
|
|
|
|
if not lines:
|
|
|
|
continue
|
|
|
|
|
2020-12-03 22:04:55 +01:00
|
|
|
lqt_create = {
|
|
|
|
'start_period': start_period,
|
|
|
|
'end_period': end_period,
|
|
|
|
'employee': employee.id,
|
|
|
|
'contract': contract.id,
|
|
|
|
'kind': kind,
|
|
|
|
'liquidation_date': liquidation_date,
|
|
|
|
'time_contracting': None,
|
|
|
|
'state': 'draft',
|
|
|
|
'company': company_id,
|
|
|
|
'description': description,
|
|
|
|
'currency': currency_id,
|
2021-02-04 16:45:22 +01:00
|
|
|
'account': account_id,
|
|
|
|
'party_to_pay': self.start.party_to_pay.id if self.start.party_to_pay else None
|
2020-12-03 22:04:55 +01:00
|
|
|
}
|
2020-12-09 21:49:53 +01:00
|
|
|
liq, = Liquidation.create([lqt_create])
|
|
|
|
liq.time_contracting = liq.on_change_with_time_contracting()
|
|
|
|
liq.save()
|
2021-02-04 20:15:05 +01:00
|
|
|
to_liquidation.append(liq)
|
2020-06-10 22:59:25 +02:00
|
|
|
|
2021-02-04 20:15:05 +01:00
|
|
|
Liquidation.compute_liquidation(to_liquidation)
|
2020-06-10 22:59:25 +02:00
|
|
|
return 'end'
|
2020-06-17 04:30:38 +02:00
|
|
|
|
|
|
|
|
|
|
|
class MoveProvisionBonusServiceStart(ModelView):
|
|
|
|
'Move Privision Bonus Service Start'
|
|
|
|
__name__ = 'staff.move_provision_bonus_service.start'
|
|
|
|
period = fields.Many2One('staff.payroll.period', 'Period',
|
|
|
|
required=True, domain=[('state', '=', 'open')])
|
|
|
|
description = fields.Char('Description', required=True)
|
|
|
|
company = fields.Many2One('company.company', 'Company',
|
|
|
|
required=True)
|
|
|
|
wage_type = fields.Many2One('staff.wage_type', 'Wage Types', domain=[
|
|
|
|
('type_concept', '=', 'bonus_service')
|
|
|
|
], required=True)
|
|
|
|
category = fields.Many2One('staff.employee_category', 'Category')
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_company():
|
|
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
|
|
|
|
|
|
class MoveProvisionBonusService(Wizard):
|
|
|
|
'Move Provision Bonus Service'
|
|
|
|
__name__ = 'staff.move_provision_bonus_service'
|
|
|
|
start = StateView('staff.move_provision_bonus_service.start',
|
|
|
|
'staff_payroll_co.move_provision_bonus_service_view_form', [
|
|
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
|
|
Button('Accept', 'open_', 'tryton-ok', default=True),
|
|
|
|
])
|
|
|
|
open_ = StateTransition()
|
|
|
|
|
|
|
|
def transition_open_(self):
|
|
|
|
pool = Pool()
|
|
|
|
Contract = pool.get('staff.contract')
|
|
|
|
Move = pool.get('account.move')
|
|
|
|
AccountPeriod = pool.get('account.period')
|
|
|
|
configuration = Pool().get('staff.configuration')(1)
|
|
|
|
journal_id = None
|
|
|
|
if configuration and configuration.default_journal:
|
|
|
|
journal_id = configuration.default_journal.id
|
|
|
|
_end_date = self.start.period.end
|
|
|
|
_company = self.start.company
|
|
|
|
provision_wage = self.start.wage_type
|
|
|
|
period_days = (self.start.period.end - self.start.period.start).days + 1
|
|
|
|
|
|
|
|
dom_contract = [
|
2020-11-04 02:24:58 +01:00
|
|
|
['AND', ['OR', [
|
2020-06-17 04:30:38 +02:00
|
|
|
('end_date', '>', self.start.period.start),
|
2020-11-04 02:24:58 +01:00
|
|
|
], [
|
2020-06-17 04:30:38 +02:00
|
|
|
('end_date', '=', None),
|
|
|
|
],
|
2020-11-04 02:24:58 +01:00
|
|
|
]],
|
2020-06-17 04:30:38 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
if self.start.category:
|
|
|
|
dom_contract.append(
|
|
|
|
('employee.category', '=', self.start.category.id)
|
|
|
|
)
|
|
|
|
for contract in Contract.search(dom_contract):
|
2020-06-17 16:04:57 +02:00
|
|
|
period_in_month = 1 if period_days > 15 else 2
|
2020-06-17 04:30:38 +02:00
|
|
|
salary_amount = contract.get_salary_in_date(_end_date)
|
|
|
|
base_ = salary_amount
|
|
|
|
move_lines = []
|
|
|
|
employee = contract.employee
|
|
|
|
|
|
|
|
for concept in contract.employee.mandatory_wages:
|
|
|
|
if concept.wage_type and concept.wage_type.salary_constitute:
|
|
|
|
if concept.wage_type.type_concept == 'transport':
|
2020-06-17 16:04:57 +02:00
|
|
|
base_ += concept.wage_type.compute_unit_price({'salary': 0}) * concept.wage_type.default_quantity
|
2020-06-17 04:30:38 +02:00
|
|
|
if concept.fix_amount:
|
|
|
|
base_ += concept.fix_amount
|
|
|
|
|
|
|
|
period_id = AccountPeriod.find(_company.id, date=_end_date)
|
|
|
|
provision_amount = provision_wage.compute_unit_price(
|
2020-06-17 16:04:57 +02:00
|
|
|
{'salary': (round((base_ / period_in_month), 2))}
|
2020-11-04 02:24:58 +01:00
|
|
|
)
|
2020-06-17 04:30:38 +02:00
|
|
|
move_lines.extend([
|
|
|
|
{
|
|
|
|
'debit': provision_amount,
|
|
|
|
'credit': 0,
|
|
|
|
'party': employee.party.id,
|
|
|
|
'account': provision_wage.debit_account.id
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'debit': 0,
|
|
|
|
'credit': provision_amount,
|
|
|
|
'party': employee.party.id,
|
2020-06-17 16:28:55 +02:00
|
|
|
'account': provision_wage.credit_account.id
|
2020-06-17 04:30:38 +02:00
|
|
|
}
|
|
|
|
])
|
|
|
|
move, = Move.create([{
|
|
|
|
'journal': journal_id,
|
|
|
|
# 'origin': str(contract),
|
|
|
|
'period': period_id,
|
|
|
|
'company': _company.id,
|
|
|
|
'date': _end_date,
|
|
|
|
'state': 'draft',
|
|
|
|
'description': self.start.description,
|
|
|
|
'lines': [('create', move_lines)],
|
|
|
|
}])
|
|
|
|
|
|
|
|
return 'end'
|
2021-02-23 22:47:31 +01:00
|
|
|
|
|
|
|
|
|
|
|
class LiquidationExportStart(ModelView):
|
|
|
|
'Liquidation Export Start'
|
|
|
|
__name__ = 'staff.liquidation_export.start'
|
|
|
|
start_date = fields.Date('Start Date', required=True)
|
|
|
|
end_date = fields.Date('End Date', required=True)
|
|
|
|
company = fields.Many2One('company.company', 'Company', required=True)
|
|
|
|
department = fields.Many2One('company.department', 'Department')
|
|
|
|
kind = fields.Selection([
|
|
|
|
('contract', 'Contract'),
|
|
|
|
('bonus_service', 'Bonus Service'),
|
|
|
|
('interest', 'Interest'),
|
|
|
|
('unemployment', 'Unemployment'),
|
|
|
|
('vacation', 'Vacation'),
|
|
|
|
], 'Kind')
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def default_company():
|
|
|
|
return Transaction().context.get('company')
|
|
|
|
|
|
|
|
@fields.depends('start_date')
|
|
|
|
def on_change_with_end_date(self, name=None):
|
|
|
|
if self.start_date:
|
|
|
|
return self.start_date
|
|
|
|
|
|
|
|
|
|
|
|
class LiquidationExport(Wizard):
|
|
|
|
'Liquidation Export'
|
|
|
|
__name__ = 'staff.liquidation_export'
|
|
|
|
start = StateView('staff.liquidation_export.start',
|
|
|
|
'staff_payroll_co.liquidation_start_view_form', [
|
|
|
|
Button('Cancel', 'end', 'tryton-cancel'),
|
|
|
|
Button('Print', 'print_', 'tryton-ok', default=True),
|
|
|
|
])
|
|
|
|
print_ = StateReport('staff.liquidation_export.report')
|
|
|
|
|
|
|
|
def do_print_(self, action):
|
|
|
|
department_id = None
|
|
|
|
if self.start.department:
|
|
|
|
department_id = self.start.department.id
|
|
|
|
data = {
|
2021-06-09 18:07:42 +02:00
|
|
|
'ids': [],
|
2021-02-23 22:47:31 +01:00
|
|
|
'company': self.start.company.id,
|
|
|
|
'start_date': self.start.start_date,
|
|
|
|
'end_date': self.start.end_date,
|
|
|
|
'department': department_id,
|
|
|
|
'kind': self.start.kind,
|
|
|
|
}
|
|
|
|
return action, data
|
|
|
|
|
|
|
|
def transition_print_(self):
|
|
|
|
return 'end'
|
|
|
|
|
|
|
|
|
|
|
|
class LiquidationExportReport(Report):
|
|
|
|
__name__ = 'staff.liquidation_export.report'
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_domain_liquidation(cls, data):
|
|
|
|
dom_liquidation = []
|
|
|
|
|
|
|
|
return dom_liquidation
|
|
|
|
|
|
|
|
@classmethod
|
2021-06-09 18:07:42 +02:00
|
|
|
def get_context(cls, records, header, data):
|
|
|
|
report_context = super().get_context(records, header, data)
|
2021-02-23 22:47:31 +01:00
|
|
|
pool = Pool()
|
|
|
|
user = pool.get('res.user')(Transaction().user)
|
|
|
|
Liquidation = pool.get('staff.liquidation')
|
|
|
|
Department = pool.get('company.department')
|
|
|
|
|
|
|
|
dom_liq = cls.get_domain_liquidation(data)
|
|
|
|
dom_liq.append(('state', 'in', ['processed', 'posted', 'draft']),)
|
|
|
|
dom_liq.append(('liquidation_date', '>=', data['start_date']),)
|
|
|
|
dom_liq.append(('liquidation_date', '<=', data['end_date']),)
|
|
|
|
|
|
|
|
if data['department']:
|
2021-07-13 22:35:41 +02:00
|
|
|
dom_liq.append(('employee.department', '=', data['department']),)
|
2021-02-23 22:47:31 +01:00
|
|
|
department = Department(data['department']).name
|
|
|
|
else:
|
|
|
|
department = None
|
|
|
|
|
|
|
|
if data['kind']:
|
|
|
|
dom_liq.append(('kind', '=', data['kind']))
|
|
|
|
|
|
|
|
liquidations = Liquidation.search(dom_liq)
|
|
|
|
|
|
|
|
# default_vals = cls.default_values()
|
|
|
|
sum_gross_payments = []
|
|
|
|
sum_total_deductions = []
|
|
|
|
sum_net_payment = []
|
|
|
|
parties = {}
|
|
|
|
|
|
|
|
payments = ['interest', 'holidays', 'bonus_service', 'unemployment']
|
|
|
|
for liquidation in liquidations:
|
|
|
|
employee_id = liquidation.employee.id
|
|
|
|
if employee_id not in parties.keys():
|
|
|
|
position_employee = liquidation.employee.position.name if liquidation.employee.position else ''
|
|
|
|
position_contract = liquidation.contract.position.name if liquidation.contract and liquidation.contract.position else ''
|
|
|
|
parties[employee_id] = {}
|
|
|
|
parties[employee_id]['employee_code'] = liquidation.employee.code
|
|
|
|
parties[employee_id]['employee_contract'] = liquidation.contract.id
|
|
|
|
parties[employee_id]['employee'] = liquidation.employee.party.name
|
|
|
|
parties[employee_id]['employee_id_number'] = liquidation.employee.party.id_number
|
|
|
|
parties[employee_id]['employee_position'] = position_contract or position_employee or ''
|
|
|
|
|
|
|
|
for line in liquidation.lines:
|
2022-01-05 17:43:27 +01:00
|
|
|
concept = None
|
2021-02-23 22:47:31 +01:00
|
|
|
if line.wage.type_concept in (payments):
|
|
|
|
concept = line.wage.type_concept
|
|
|
|
else:
|
2022-01-05 17:51:14 +01:00
|
|
|
if line.wage.definition == 'payment':
|
2021-02-23 22:47:31 +01:00
|
|
|
concept = 'others_payments'
|
|
|
|
elif line.wage.definition == 'deduction' or \
|
|
|
|
line.wage.definition == 'discount' and \
|
|
|
|
line.wage.receipt:
|
|
|
|
concept = 'others_deductions'
|
|
|
|
if not concept:
|
2021-06-09 18:07:42 +02:00
|
|
|
raise WageTypeConceptError(
|
|
|
|
gettext('staff_payroll_co.msg_type_concept_not_exists', s=line.wage.name))
|
2021-02-23 22:47:31 +01:00
|
|
|
if concept not in parties[employee_id].keys():
|
|
|
|
parties[employee_id][concept] = 0
|
|
|
|
|
|
|
|
parties[employee_id][concept] += line.amount
|
|
|
|
parties[employee_id]['time_contracting'] = liquidation.time_contracting
|
|
|
|
parties[employee_id]['net_payment'] = liquidation.net_payment
|
|
|
|
parties[employee_id]['gross_payments'] = liquidation.gross_payments
|
|
|
|
parties[employee_id]['total_deductions'] = liquidation.total_deductions
|
|
|
|
sum_gross_payments.append(liquidation.gross_payments)
|
|
|
|
sum_total_deductions.append(liquidation.total_deductions)
|
|
|
|
sum_net_payment.append(liquidation.net_payment)
|
|
|
|
|
|
|
|
employee_dict = {e['employee']: e for e in parties.values()}
|
|
|
|
|
|
|
|
report_context['records'] = sorted(employee_dict.items(), key=lambda t: t[0])
|
|
|
|
report_context['department'] = department
|
|
|
|
report_context['start_date'] = data['start_date']
|
|
|
|
report_context['end_date'] = data['end_date']
|
|
|
|
report_context['company'] = user.company
|
|
|
|
report_context['user'] = user
|
|
|
|
report_context['sum_gross_payments'] = sum(sum_gross_payments)
|
|
|
|
report_context['sum_net_payment'] = sum(sum_net_payment)
|
|
|
|
report_context['sum_total_deductions'] = sum(sum_total_deductions)
|
|
|
|
return report_context
|