Files
public/rsbu_payroll/models/hr_payslip_run.py

161 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class HrPayslipRun(models.Model):
"""Реестр зарплаты (зарплатный проект банка).
Расширяет штатный «Пакет расчётных листков» до реестра на выплату:
банк одним платежом зачисляет общую сумму сотрудникам (в выписке одна
строка от банка), а реестр раскладывает её на выплаты по каждому
сотруднику (Дт 70 — Кт 51) одной кнопкой.
"""
_inherit = 'hr.payslip.run'
payment_bank_partner_id = fields.Many2one(
'res.partner', string='Банк (зарплатный проект)',
help='Контрагент-банк зарплатного проекта (напр. Т-Банк), от которого '
'приходит единый платёж по реестру.')
payment_date = fields.Date(
string='Дата выплаты',
default=fields.Date.context_today)
bank_fee_amount = fields.Monetary(
string='Комиссия банка', currency_field='currency_id', default=0.0)
currency_id = fields.Many2one(
'res.currency', related='company_id.currency_id', readonly=True)
total_net = fields.Monetary(
string='Итого к перечислению', currency_field='currency_id',
compute='_compute_total_net', store=False,
help='Сумма «к выплате» (правило NET) по проведённым расчётным '
'листкам реестра.')
rsbu_payout_move_id = fields.Many2one(
'rsbu.move', string='РСБУ-проводка выплаты', copy=False, readonly=True)
payout_state = fields.Selection(
[('to_pay', 'К выплате'), ('paid', 'Выплачено')],
string='Статус выплаты', default='to_pay', copy=False, readonly=True)
@api.depends('slip_ids.line_ids.total', 'slip_ids.state')
def _compute_total_net(self):
for run in self:
total = 0.0
for slip in run.slip_ids.filtered(lambda s: s.state == 'done'):
total += run._slip_net(slip)
run.total_net = total
@staticmethod
def _slip_net(slip):
"""Сумма «к выплате» по расчётному листку (правило NET)."""
net_lines = slip.line_ids.filtered(lambda l: l.code == 'NET')
return sum(net_lines.mapped('total'))
# ------------------------------------------------------------------
# Проводки выплаты
# ------------------------------------------------------------------
def _rsbu_account(self, code):
"""Найти счёт РСБУ по коду в разрезе компании реестра."""
self.ensure_one()
account = self.env['rsbu.account'].search([
('code', '=', code),
('company_id', '=', self.company_id.id),
], limit=1)
if not account:
raise UserError(_(
'В плане счетов РСБУ компании «%(company)s» не найден счёт %(code)s.',
company=self.company_id.display_name, code=code))
return account
def action_rsbu_pay(self):
"""Сформировать РСБУ-проводку выплаты по реестру (Дт 70 — Кт 51)."""
for run in self:
if run.rsbu_payout_move_id:
# Идемпотентность: повторное нажатие не дублирует.
continue
done_slips = run.slip_ids.filtered(lambda s: s.state == 'done')
if not done_slips:
raise UserError(_(
'В реестре «%s» нет проведённых расчётных листков.')
% run.display_name)
acc_70 = run._rsbu_account('70')
acc_51 = run._rsbu_account('51')
lines = []
total_pay = 0.0
for slip in done_slips:
net = run._slip_net(slip)
if not net:
continue
if not slip.employee_id:
raise UserError(_(
'В расчётном листке %s не указан сотрудник.') % slip.number)
# Дт 70 — закрытие задолженности перед сотрудником.
lines.append({
'account_id': acc_70.id,
'name': _('Выплата зарплаты: %s') % slip.employee_id.name,
'debit': net,
'employee_id': slip.employee_id.id,
})
total_pay += net
if not lines:
raise UserError(_(
'Нет сумм к выплате по реестру «%s».') % run.display_name)
fee = run.bank_fee_amount or 0.0
# Кт 51 — единый платёж банка (сумма выплат + комиссия).
lines.append({
'account_id': acc_51.id,
'name': _('Перечисление зарплаты (реестр %s)') % run.name,
'credit': total_pay + fee,
})
if fee:
acc_fee = run._rsbu_account('91')
lines.append({
'account_id': acc_fee.id,
'name': _('Комиссия банка по реестру %s') % run.name,
'debit': fee,
})
usn_recognition = (
'expense'
if run.company_id.rsbu_usn_mode == 'usn_income_expense'
else 'none')
move = self.env['rsbu.move'].sudo().create({
'date': run.payment_date or fields.Date.context_today(run),
'company_id': run.company_id.id,
'ref': _('Выплата зарплаты по реестру %s') % run.name,
'source_model': run._name,
'source_res_id': run.id,
'usn_recognition': usn_recognition,
'line_ids': [(0, 0, line) for line in lines],
})
move.action_post()
run.write({
'rsbu_payout_move_id': move.id,
'payout_state': 'paid',
})
return True
def action_rsbu_pay_cancel(self):
"""Отменить проводку выплаты (сторно/удаление)."""
for run in self:
move = run.rsbu_payout_move_id.sudo()
if move:
run.rsbu_payout_move_id = False
run.payout_state = 'to_pay'
if move.state == 'posted':
move.action_draft()
move.unlink()
return True
def action_view_rsbu_payout_move(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': _('РСБУ-проводка выплаты'),
'res_model': 'rsbu.move',
'view_mode': 'form',
'res_id': self.rsbu_payout_move_id.id,
}