Initial commit
This commit is contained in:
213
fusion_payroll/wizards/payroll_cheque_print_wizard.py
Normal file
213
fusion_payroll/wizards/payroll_cheque_print_wizard.py
Normal file
@@ -0,0 +1,213 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Payroll Cheque Print Wizard
|
||||
===========================
|
||||
Wizard that appears after payroll submission when cheques need to be printed.
|
||||
"""
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class PayrollChequePrintWizard(models.TransientModel):
|
||||
"""Wizard to print cheques after payroll submission."""
|
||||
_name = 'payroll.cheque.print.wizard'
|
||||
_description = 'Print Payroll Cheques'
|
||||
|
||||
payslip_run_id = fields.Many2one(
|
||||
'hr.payslip.run',
|
||||
string='Payslip Batch',
|
||||
readonly=True,
|
||||
)
|
||||
cheque_ids = fields.Many2many(
|
||||
'payroll.cheque',
|
||||
string='Cheques to Print',
|
||||
)
|
||||
cheque_count = fields.Integer(string='Number of Cheques')
|
||||
|
||||
# Starting cheque number (optional override)
|
||||
starting_cheque_number = fields.Char(
|
||||
string='Starting Cheque Number',
|
||||
help='Leave blank to use automatic numbering',
|
||||
)
|
||||
|
||||
# Preview of cheques
|
||||
cheque_preview = fields.Html(
|
||||
string='Cheque Preview',
|
||||
compute='_compute_cheque_preview',
|
||||
)
|
||||
|
||||
@api.depends('cheque_ids')
|
||||
def _compute_cheque_preview(self):
|
||||
for wizard in self:
|
||||
if not wizard.cheque_ids:
|
||||
wizard.cheque_preview = '<p>No cheques to print</p>'
|
||||
continue
|
||||
|
||||
html = '<table class="table table-sm"><thead><tr>'
|
||||
html += '<th>Employee</th><th>Amount</th><th>Pay Period</th>'
|
||||
html += '</tr></thead><tbody>'
|
||||
|
||||
for cheque in wizard.cheque_ids:
|
||||
html += f'<tr><td>{cheque.employee_id.name}</td>'
|
||||
html += f'<td>${cheque.amount:,.2f}</td>'
|
||||
html += f'<td>{cheque.pay_period_display or ""}</td></tr>'
|
||||
|
||||
html += '</tbody></table>'
|
||||
wizard.cheque_preview = html
|
||||
|
||||
def action_print_all(self):
|
||||
"""Print all cheques."""
|
||||
self.ensure_one()
|
||||
|
||||
if not self.cheque_ids:
|
||||
raise UserError(_("No cheques to print."))
|
||||
|
||||
# Assign numbers if custom starting number provided
|
||||
if self.starting_cheque_number:
|
||||
try:
|
||||
start_num = int(self.starting_cheque_number)
|
||||
for i, cheque in enumerate(self.cheque_ids.sorted(key=lambda c: c.employee_id.name)):
|
||||
cheque.cheque_number = str(start_num + i).zfill(6)
|
||||
except ValueError:
|
||||
raise UserError(_("Starting cheque number must be numeric."))
|
||||
else:
|
||||
# Use sequence
|
||||
for cheque in self.cheque_ids:
|
||||
if not cheque.cheque_number:
|
||||
cheque.action_assign_number()
|
||||
|
||||
# Mark all as printed
|
||||
self.cheque_ids.write({
|
||||
'state': 'printed',
|
||||
'printed_date': fields.Datetime.now(),
|
||||
})
|
||||
|
||||
# Generate report
|
||||
return self.env.ref('fusion_payroll.action_report_payroll_cheque').report_action(self.cheque_ids)
|
||||
|
||||
def action_skip_print(self):
|
||||
"""Skip printing and go to payslip view."""
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Payslips'),
|
||||
'res_model': 'hr.payslip.run',
|
||||
'res_id': self.payslip_run_id.id,
|
||||
'view_mode': 'form',
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
def action_view_cheques(self):
|
||||
"""View cheques list."""
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Cheques'),
|
||||
'res_model': 'payroll.cheque',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('id', 'in', self.cheque_ids.ids)],
|
||||
'target': 'current',
|
||||
}
|
||||
|
||||
|
||||
class HrPayslipRunCheque(models.Model):
|
||||
"""Extend payslip batch with cheque functionality."""
|
||||
_inherit = 'hr.payslip.run'
|
||||
|
||||
cheque_ids = fields.One2many(
|
||||
'payroll.cheque',
|
||||
'payslip_run_id',
|
||||
string='Cheques',
|
||||
)
|
||||
cheque_count = fields.Integer(
|
||||
string='Cheque Count',
|
||||
compute='_compute_cheque_count',
|
||||
)
|
||||
cheques_to_print = fields.Integer(
|
||||
string='Cheques to Print',
|
||||
compute='_compute_cheque_count',
|
||||
)
|
||||
|
||||
@api.depends('cheque_ids', 'cheque_ids.state')
|
||||
def _compute_cheque_count(self):
|
||||
for batch in self:
|
||||
batch.cheque_count = len(batch.cheque_ids)
|
||||
batch.cheques_to_print = len(batch.cheque_ids.filtered(lambda c: c.state == 'draft'))
|
||||
|
||||
def action_view_cheques(self):
|
||||
"""Open cheques for this batch."""
|
||||
self.ensure_one()
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Cheques'),
|
||||
'res_model': 'payroll.cheque',
|
||||
'view_mode': 'list,form',
|
||||
'domain': [('payslip_run_id', '=', self.id)],
|
||||
'context': {'default_payslip_run_id': self.id},
|
||||
}
|
||||
|
||||
def action_print_cheques(self):
|
||||
"""Print all draft cheques for this batch."""
|
||||
self.ensure_one()
|
||||
|
||||
cheques = self.cheque_ids.filtered(lambda c: c.state == 'draft')
|
||||
|
||||
if not cheques:
|
||||
# Check if there are employees needing cheques
|
||||
employees_needing_cheques = self.slip_ids.filtered(
|
||||
lambda s: hasattr(s.employee_id, 'payment_method') and
|
||||
s.employee_id.payment_method == 'cheque'
|
||||
).mapped('employee_id')
|
||||
|
||||
if employees_needing_cheques:
|
||||
# Create cheques
|
||||
for slip in self.slip_ids:
|
||||
if hasattr(slip.employee_id, 'payment_method') and \
|
||||
slip.employee_id.payment_method == 'cheque':
|
||||
self.env['payroll.cheque'].create_from_payslip(slip)
|
||||
|
||||
cheques = self.cheque_ids.filtered(lambda c: c.state == 'draft')
|
||||
|
||||
if not cheques:
|
||||
from odoo.exceptions import UserError
|
||||
raise UserError(_("No cheques to print. Make sure employees have payment method set to 'Cheque'."))
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Print Cheques'),
|
||||
'res_model': 'payroll.cheque.print.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_payslip_run_id': self.id,
|
||||
'default_cheque_ids': [(6, 0, cheques.ids)],
|
||||
'default_cheque_count': len(cheques),
|
||||
},
|
||||
}
|
||||
|
||||
def action_create_cheques(self):
|
||||
"""Create cheques for all employees paid by cheque."""
|
||||
self.ensure_one()
|
||||
|
||||
created_count = 0
|
||||
for slip in self.slip_ids:
|
||||
if hasattr(slip.employee_id, 'payment_method') and \
|
||||
slip.employee_id.payment_method == 'cheque':
|
||||
# Check if cheque already exists
|
||||
existing = self.env['payroll.cheque'].search([
|
||||
('payslip_id', '=', slip.id),
|
||||
], limit=1)
|
||||
|
||||
if not existing:
|
||||
self.env['payroll.cheque'].create_from_payslip(slip)
|
||||
created_count += 1
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Cheques Created'),
|
||||
'message': _('%d cheques created.') % created_count,
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user