trytonpsk-staff_payroll_co/liquidation.py

923 lines
34 KiB
Python
Raw Normal View History

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
2020-12-28 22:35:46 +01:00
from datetime import timedelta, date
2020-04-16 00:38:42 +02:00
from trytond import backend
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
2020-06-13 01:50:41 +02:00
from trytond.wizard import Wizard, StateView, Button, StateTransition
2020-04-16 00:38:42 +02:00
from trytond.transaction import Transaction
2020-11-04 02:24:58 +01:00
__all__ = [
'Liquidation', 'Report', 'LiquidationLine', 'LiquidationGroup',
'LiquidationLineMoveLine', 'LiquidationLineAdjustment',
'LiquidationGroupStart', 'LiquidationAdjustmentStart',
'LiquidationAdjustment'
]
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 02:24:58 +01:00
CONTRACT = [
'bonus_service', 'health', 'retirement', 'unemployment', 'interest',
'holidays'
]
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'),
2020-04-16 00:38:42 +02:00
('vacation', 'Vacation'),
], '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-02-04 16:45:22 +01:00
contract = fields.Many2One('staff.contract', 'Contract', required=True,
states=STATES, domain=[('employee', '=', Eval('employee'))])
2020-04-16 00:38:42 +02:00
account = fields.Many2One('account.account', 'Account',
required=True, domain=[
('kind', '!=', 'view'),
])
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)
2020-04-16 00:38:42 +02:00
@classmethod
def __setup__(cls):
super(Liquidation, cls).__setup__()
cls._error_messages.update({
'employee_without_salary': ('The employee does not have salary!'),
'wrong_start_end': ('The date end can not smaller than date start'),
'sequence_missing': ('Sequence liquidation is missing!'),
'payroll_not_posted': ('The employee has payrolls does not posted!'),
})
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({
'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
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_kind():
return 'contract'
@staticmethod
def default_state():
return 'draft'
@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:
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()
@classmethod
2020-11-27 06:34:19 +01:00
def create_withholding(cls, liquidation):
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-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-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
def get_dates(self, name):
if name == 'start':
values = [self.start_period.start]
if self.employee.contract.start_date:
values.append(self.employee.contract.start_date)
res = max(values)
if name == 'end':
values = [self.end_period.end]
if self.employee.contract.end_date:
values.append(self.employee.contract.end_date)
res = min(values)
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
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,
#'origin': str(self),
'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 (
ml.account.kind not in ('payable', 'receivable')):
2020-04-16 00:38:42 +02:00
continue
to_reconcile = [ml]
to_reconcile.extend(grouped[ml.account.id]['lines'])
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)
if moveline.account.id not in grouped.keys():
grouped[moveline.account.id] = {
'amount': [],
'description': line.description,
'lines': [],
}
grouped[moveline.account.id]['amount'].append(moveline.credit)
grouped[moveline.account.id]['lines'].append(moveline)
amount.append(moveline.credit)
for adjust in line.adjustments:
if adjust.account.id not in grouped.keys():
grouped[adjust.account.id] = {
'amount': [],
'description': adjust.description,
2020-04-16 00:38:42 +02:00
'lines': [],
}
grouped[adjust.account.id]['amount'].append(adjust.amount)
amount.append(adjust.amount)
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'],
2020-12-03 19:58:38 +01:00
account_id, debit=debit, credit=credit))
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
2021-02-04 16:45:22 +01:00
def _prepare_line(self, description, account_id, debit=_ZERO, credit=_ZERO, party_to_pay=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
}
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
if self.contract.start_date > self.start_period.start:
date_start = self.contract.start_date
else:
date_start = self.start_period.start
if self.contract.futhermores:
date_end_contract = self.contract.finished_date
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
else:
date_end = self.end_period.end
return date_start, date_end
def set_liquidation_lines(self):
pool = Pool()
Payroll = pool.get('staff.payroll')
Liquidation = pool.get('staff.liquidation')
MoveLine = pool.get('account.move.line')
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),
])
wages = {}
days = 0
moves_ids = []
for payroll in payrolls:
if not payroll.move:
continue
moves_ids.append(payroll.move.id)
for payroll in payrolls:
days += payroll.worked_days
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
elif self.kind == 'interest':
if l.wage_type.type_concept != 'interest':
continue
elif self.kind == 'vacation':
if l.wage_type.type_concept != 'holidays':
continue
2021-02-04 16:45:22 +01:00
elif self.kind == 'unemployment':
if l.wage_type.type_concept != 'unemployment':
continue
2020-04-16 00:38:42 +02:00
else:
if l.wage_type.type_concept not in BONUS_SERVICE:
continue
if not l.wage_type.contract_finish:
continue
if l.wage_type.id not in wages:
account_id = None
if l.wage_type.credit_account:
account_id = l.wage_type.credit_account.id
lines = MoveLine.search([
2020-08-31 18:03:14 +02:00
# ('move', 'in', moves_ids),
2020-04-16 00:38:42 +02:00
('account', '=', account_id),
('party', '=', self.employee.party.id),
('reconciliation', '=', None),
])
# 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)
2020-04-16 00:38:42 +02:00
values = []
lines_to_reconcile = []
for line in lines:
values.append(abs(line.debit - line.credit))
lines_to_reconcile.append(line.id)
wages[l.wage_type.id] = {
'sequence': l.wage_type.sequence,
'wage': l.wage_type.id,
'description': l.wage_type.name,
'amount': sum(values),
'days': self.time_contracting,
'account': account_id,
'move_lines': [('add', lines_to_reconcile)],
2021-02-04 16:45:22 +01:00
'party_to_pay': self.party_to_pay,
2020-04-16 00:38:42 +02:00
}
lines_to_create = wages.values()
Liquidation.write([self], {
'lines': [('create', lines_to_create)]}
)
@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:
2020-12-08 12:45:42 +01:00
date_start, date_end = self._get_dates()
delta = self.get_time_contracting(date_start, date_end)
2020-04-16 00:38:42 +02:00
return delta
2020-12-08 12:45:42 +01:00
def get_time_contracting(self, start_date, end_date):
sum_days = 0
if end_date.day == 31:
2020-12-28 22:35:46 +01:00
end_date = date(end_date.year, end_date.month, 30)
2020-04-16 00:38:42 +02:00
2020-12-08 12:45:42 +01:00
delta = (end_date - start_date).days + 1
if start_date.month == end_date.month:
return delta
for d in range(delta):
next_date = start_date + timedelta(days=d)
if next_date.day == 31:
continue
if next_date.month == 2:
if next_date.day == 29:
continue
elif next_date.day == 28:
sum_days += 2
else:
sum_days += 1
else:
sum_days += 1
return sum_days
2020-04-16 00:38:42 +02:00
def set_number(self):
if self.number:
return
pool = Pool()
Sequence = pool.get('ir.sequence')
Configuration = pool.get('staff.configuration')
configuration = Configuration(1)
if not configuration.staff_liquidation_sequence:
self.raise_user_error('sequence_missing',)
seq = configuration.staff_liquidation_sequence.id
self.write([self], {'number': Sequence.get_id(seq)})
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):
return self.currency.round(self.gross_payments + self.total_deductions)
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')
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):
TableHandler = backend.get('TableHandler')
table = TableHandler(cls, module_name)
# Migration from 4.0: remove hoard_amount
if table.column_exist('hoard_amount'):
table.drop_column('hoard_amount')
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
@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
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)),
('kind', '!=', 'view'),
])
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)),
('kind', '!=', 'view'),
])
description = fields.Char('Description', required=True)
@fields.depends('wage_type', 'account')
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()
@classmethod
def __setup__(cls):
super(LiquidationAdjustment, cls).__setup__()
cls._error_messages.update({
'liquidation_with_move': ('The liquidation Nro "%s" have move!'),
})
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
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:
self.raise_user_error('liquidation_with_move', (liquidation.number))
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,
'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',
required=True)
end_period = fields.Many2One('staff.payroll.period', 'End Period',
required=True, depends=['start_period'])
kind = fields.Selection([
('contract', 'Contract'),
('bonus_service', 'Bonus Service'),
('interest', 'Interest'),
('vacation', '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=[
('kind', '=', 'payable'),
2020-06-10 22:59:25 +02:00
('company', '=', Eval('company'))
])
2020-12-03 22:04:55 +01: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')
2020-06-10 22:59:25 +02:00
compute_liquidations = []
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
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:
employees = Employee.search([])
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),
2020-06-12 06:41:33 +02:00
])
2020-12-03 22:04:55 +01:00
if not contracts:
continue
contract = contracts[0]
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()
compute_liquidations.append(liq)
2020-06-10 22:59:25 +02:00
Liquidation.compute_liquidation(compute_liquidations)
return 'end'
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', [
('end_date', '>', self.start.period.start),
2020-11-04 02:24:58 +01:00
], [
('end_date', '=', None),
],
2020-11-04 02:24:58 +01: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
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
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
)
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
}
])
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'