Public release from ruodoo-project: 19.0 - 2026-11-01 22:52:25 UTC

This commit is contained in:
CI Publish Bot
2026-11-01 22:54:30 +00:00
commit 26250aa580
2492 changed files with 664277 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
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,
}