Initial commit
This commit is contained in:
614
fusion_payroll/models/payroll_cheque.py
Normal file
614
fusion_payroll/models/payroll_cheque.py
Normal file
@@ -0,0 +1,614 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Payroll Cheque Management
|
||||
=========================
|
||||
Custom cheque printing for payroll with configurable layouts.
|
||||
"""
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from num2words import num2words
|
||||
|
||||
|
||||
class PayrollCheque(models.Model):
|
||||
"""
|
||||
Payroll Cheque Record
|
||||
Tracks cheques issued for payroll payments.
|
||||
"""
|
||||
_name = 'payroll.cheque'
|
||||
_description = 'Payroll Cheque'
|
||||
_order = 'cheque_date desc, cheque_number desc'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
name = fields.Char(
|
||||
string='Reference',
|
||||
compute='_compute_name',
|
||||
store=True,
|
||||
)
|
||||
cheque_number = fields.Char(
|
||||
string='Cheque Number',
|
||||
readonly=True,
|
||||
copy=False,
|
||||
tracking=True,
|
||||
)
|
||||
cheque_date = fields.Date(
|
||||
string='Cheque Date',
|
||||
required=True,
|
||||
default=fields.Date.context_today,
|
||||
tracking=True,
|
||||
)
|
||||
|
||||
# Payee Information
|
||||
employee_id = fields.Many2one(
|
||||
'hr.employee',
|
||||
string='Employee',
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
payment_method_display = fields.Char(
|
||||
string='Payment Method',
|
||||
compute='_compute_payment_method_display',
|
||||
)
|
||||
payee_name = fields.Char(
|
||||
string='Payee Name',
|
||||
compute='_compute_payee_info',
|
||||
store=True,
|
||||
)
|
||||
payee_address = fields.Text(
|
||||
string='Payee Address',
|
||||
compute='_compute_payee_info',
|
||||
store=True,
|
||||
)
|
||||
|
||||
# Amount
|
||||
amount = fields.Monetary(
|
||||
string='Amount',
|
||||
currency_field='currency_id',
|
||||
required=True,
|
||||
tracking=True,
|
||||
)
|
||||
amount_in_words = fields.Char(
|
||||
string='Amount in Words',
|
||||
compute='_compute_amount_in_words',
|
||||
)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency',
|
||||
string='Currency',
|
||||
default=lambda self: self.env.company.currency_id,
|
||||
)
|
||||
|
||||
# Related Records
|
||||
payslip_id = fields.Many2one(
|
||||
'hr.payslip',
|
||||
string='Payslip',
|
||||
ondelete='set null',
|
||||
)
|
||||
payslip_run_id = fields.Many2one(
|
||||
'hr.payslip.run',
|
||||
string='Payslip Batch',
|
||||
ondelete='set null',
|
||||
)
|
||||
|
||||
# Bank Account
|
||||
bank_account_id = fields.Many2one(
|
||||
'res.partner.bank',
|
||||
string='Bank Account',
|
||||
domain="[('company_id', '=', company_id)]",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
|
||||
# Pay Period Info
|
||||
pay_period_start = fields.Date(string='Pay Period Start')
|
||||
pay_period_end = fields.Date(string='Pay Period End')
|
||||
pay_period_display = fields.Char(
|
||||
string='Pay Period',
|
||||
compute='_compute_pay_period_display',
|
||||
)
|
||||
|
||||
# Memo
|
||||
memo = fields.Text(string='Memo')
|
||||
|
||||
# Status
|
||||
state = fields.Selection([
|
||||
('draft', 'Draft'),
|
||||
('printed', 'Printed'),
|
||||
('voided', 'Voided'),
|
||||
('cashed', 'Cashed'),
|
||||
], string='Status', default='draft', tracking=True)
|
||||
|
||||
printed_date = fields.Datetime(string='Printed Date', readonly=True)
|
||||
voided_date = fields.Datetime(string='Voided Date', readonly=True)
|
||||
void_reason = fields.Text(string='Void Reason')
|
||||
|
||||
# ==================== COMPUTED FIELDS ====================
|
||||
|
||||
@api.depends('cheque_number', 'employee_id')
|
||||
def _compute_name(self):
|
||||
for cheque in self:
|
||||
if cheque.cheque_number and cheque.employee_id:
|
||||
cheque.name = f"CHQ-{cheque.cheque_number} - {cheque.employee_id.name}"
|
||||
elif cheque.cheque_number:
|
||||
cheque.name = f"CHQ-{cheque.cheque_number}"
|
||||
else:
|
||||
cheque.name = _('New Cheque')
|
||||
|
||||
@api.depends('employee_id')
|
||||
def _compute_payee_info(self):
|
||||
for cheque in self:
|
||||
if cheque.employee_id:
|
||||
emp = cheque.employee_id
|
||||
cheque.payee_name = emp.name
|
||||
|
||||
# Build address from Fusion Payroll fields or fallback
|
||||
address_parts = []
|
||||
|
||||
# Try Fusion Payroll home address fields
|
||||
if hasattr(emp, 'home_street') and emp.home_street:
|
||||
address_parts.append(emp.home_street)
|
||||
if hasattr(emp, 'home_street2') and emp.home_street2:
|
||||
address_parts.append(emp.home_street2)
|
||||
|
||||
city_line = []
|
||||
if hasattr(emp, 'home_city') and emp.home_city:
|
||||
city_line.append(emp.home_city)
|
||||
if hasattr(emp, 'home_province') and emp.home_province:
|
||||
city_line.append(emp.home_province)
|
||||
if hasattr(emp, 'home_postal_code') and emp.home_postal_code:
|
||||
city_line.append(emp.home_postal_code)
|
||||
|
||||
if city_line:
|
||||
address_parts.append(' '.join(city_line))
|
||||
|
||||
# Fallback to private address
|
||||
if not address_parts and emp.private_street:
|
||||
address_parts.append(emp.private_street)
|
||||
if emp.private_city:
|
||||
address_parts.append(f"{emp.private_city}, {emp.private_state_id.code or ''} {emp.private_zip or ''}")
|
||||
|
||||
cheque.payee_address = '\n'.join(address_parts) if address_parts else ''
|
||||
else:
|
||||
cheque.payee_name = ''
|
||||
cheque.payee_address = ''
|
||||
|
||||
@api.depends('amount', 'currency_id')
|
||||
def _compute_amount_in_words(self):
|
||||
for cheque in self:
|
||||
if cheque.amount:
|
||||
# Split into dollars and cents
|
||||
dollars = int(cheque.amount)
|
||||
cents = int(round((cheque.amount - dollars) * 100))
|
||||
|
||||
# Convert to words
|
||||
dollars_words = num2words(dollars, lang='en').title()
|
||||
|
||||
# Format: "One Thousand Three Hundred Fifty-Three and 47/100"
|
||||
cheque.amount_in_words = f"*****{dollars_words} and {cents:02d}/100"
|
||||
else:
|
||||
cheque.amount_in_words = ''
|
||||
|
||||
@api.depends('pay_period_start', 'pay_period_end')
|
||||
def _compute_pay_period_display(self):
|
||||
for cheque in self:
|
||||
if cheque.pay_period_start and cheque.pay_period_end:
|
||||
cheque.pay_period_display = f"{cheque.pay_period_start.strftime('%m.%d.%Y')} - {cheque.pay_period_end.strftime('%m.%d.%Y')}"
|
||||
else:
|
||||
cheque.pay_period_display = ''
|
||||
|
||||
@api.depends('employee_id', 'employee_id.payment_method')
|
||||
def _compute_payment_method_display(self):
|
||||
for cheque in self:
|
||||
if cheque.employee_id and hasattr(cheque.employee_id, 'payment_method'):
|
||||
method = cheque.employee_id.payment_method
|
||||
if method == 'cheque':
|
||||
cheque.payment_method_display = 'Cheque'
|
||||
elif method == 'direct_deposit':
|
||||
cheque.payment_method_display = 'Direct Deposit'
|
||||
else:
|
||||
cheque.payment_method_display = method or 'N/A'
|
||||
else:
|
||||
cheque.payment_method_display = 'N/A'
|
||||
|
||||
# ==================== ACTIONS ====================
|
||||
|
||||
def action_assign_number(self):
|
||||
"""Assign cheque number from sequence, checking for highest existing number."""
|
||||
for cheque in self:
|
||||
if not cheque.cheque_number:
|
||||
# Get the highest cheque number from payroll cheques
|
||||
max_payroll = self.env['payroll.cheque'].search([
|
||||
('cheque_number', '!=', False),
|
||||
('cheque_number', '!=', ''),
|
||||
('company_id', '=', cheque.company_id.id),
|
||||
], order='cheque_number desc', limit=1)
|
||||
|
||||
# Get the highest cheque number from account.payment (vendor payments)
|
||||
max_payment = 0
|
||||
if 'account.payment' in self.env:
|
||||
payments = self.env['account.payment'].search([
|
||||
('check_number', '!=', False),
|
||||
('check_number', '!=', ''),
|
||||
('company_id', '=', cheque.company_id.id),
|
||||
])
|
||||
for payment in payments:
|
||||
try:
|
||||
num = int(payment.check_number)
|
||||
if num > max_payment:
|
||||
max_payment = num
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Get highest from payroll cheques
|
||||
max_payroll_num = 0
|
||||
if max_payroll and max_payroll.cheque_number:
|
||||
try:
|
||||
max_payroll_num = int(max_payroll.cheque_number)
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# Use the higher of the two, or sequence if both are 0
|
||||
next_num = max(max_payroll_num, max_payment) + 1
|
||||
|
||||
if next_num > 1:
|
||||
# Set sequence to next number
|
||||
sequence = self.env['ir.sequence'].search([
|
||||
('code', '=', 'payroll.cheque'),
|
||||
('company_id', '=', cheque.company_id.id),
|
||||
], limit=1)
|
||||
if sequence:
|
||||
sequence.write({'number_next': next_num})
|
||||
|
||||
cheque.cheque_number = str(next_num).zfill(6)
|
||||
else:
|
||||
# Use sequence normally
|
||||
cheque.cheque_number = self.env['ir.sequence'].next_by_code('payroll.cheque') or '/'
|
||||
|
||||
def action_print_cheque(self):
|
||||
"""Always open wizard to set/change cheque number before printing."""
|
||||
self.ensure_one()
|
||||
|
||||
# Always open wizard to allow changing the cheque number
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Set Cheque Number'),
|
||||
'res_model': 'payroll.cheque.number.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {
|
||||
'default_cheque_id': self.id,
|
||||
},
|
||||
}
|
||||
|
||||
def action_void(self):
|
||||
"""Void the cheque."""
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Void Cheque'),
|
||||
'res_model': 'payroll.cheque.void.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {'default_cheque_id': self.id},
|
||||
}
|
||||
|
||||
def action_mark_cashed(self):
|
||||
"""Mark cheque as cashed."""
|
||||
self.write({'state': 'cashed'})
|
||||
|
||||
def action_reset_to_draft(self):
|
||||
"""Reset to draft (only for voided cheques)."""
|
||||
for cheque in self:
|
||||
if cheque.state == 'voided':
|
||||
cheque.write({
|
||||
'state': 'draft',
|
||||
'voided_date': False,
|
||||
'void_reason': False,
|
||||
})
|
||||
|
||||
# ==================== HELPER METHODS ====================
|
||||
|
||||
def get_pay_stub_data(self):
|
||||
"""Get pay stub data for the cheque report."""
|
||||
self.ensure_one()
|
||||
|
||||
payslip = self.payslip_id
|
||||
if not payslip:
|
||||
return {}
|
||||
|
||||
# Get payslip lines by category
|
||||
def get_line_amount(code):
|
||||
line = payslip.line_ids.filtered(lambda l: l.code == code)
|
||||
return line.total if line else 0
|
||||
|
||||
# Calculate YTD values
|
||||
year_start = self.cheque_date.replace(month=1, day=1)
|
||||
ytd_payslips = self.env['hr.payslip'].search([
|
||||
('employee_id', '=', self.employee_id.id),
|
||||
('date_from', '>=', year_start),
|
||||
('date_to', '<=', self.cheque_date),
|
||||
('state', 'in', ['done', 'paid']),
|
||||
])
|
||||
|
||||
def get_ytd_amount(code):
|
||||
total = 0
|
||||
for slip in ytd_payslips:
|
||||
line = slip.line_ids.filtered(lambda l: l.code == code)
|
||||
total += line.total if line else 0
|
||||
return total
|
||||
|
||||
# Get hourly rate
|
||||
hourly_rate = 0
|
||||
if hasattr(self.employee_id, 'hourly_rate'):
|
||||
hourly_rate = self.employee_id.hourly_rate or 0
|
||||
|
||||
# Calculate hours from payslip inputs
|
||||
regular_hours = 0
|
||||
for input_line in payslip.input_line_ids:
|
||||
if 'hour' in (input_line.code or '').lower():
|
||||
regular_hours = input_line.amount
|
||||
break
|
||||
|
||||
# Get regular pay - look for REGPAY first, then BASIC, then GROSS
|
||||
regular_pay_current = (get_line_amount('REGPAY') or get_line_amount('BASIC') or
|
||||
get_line_amount('GROSS') or payslip.basic_wage or 0)
|
||||
regular_pay_ytd = (get_ytd_amount('REGPAY') or get_ytd_amount('BASIC') or
|
||||
get_ytd_amount('GROSS') or 0)
|
||||
|
||||
# Get vacation pay
|
||||
vacation_pay_current = get_line_amount('VAC') or get_line_amount('VACATION') or 0
|
||||
vacation_pay_ytd = get_ytd_amount('VAC') or get_ytd_amount('VACATION') or 0
|
||||
|
||||
# Get stat holiday pay
|
||||
stat_pay_current = get_line_amount('STAT') or get_line_amount('STATHOLIDAY') or 0
|
||||
stat_pay_ytd = get_ytd_amount('STAT') or get_ytd_amount('STATHOLIDAY') or 0
|
||||
|
||||
# Get taxes - these are negative in payslip, so use abs()
|
||||
# First try to get from payslip lines
|
||||
income_tax_current = abs(get_line_amount('FIT') or get_line_amount('INCOMETAX') or 0)
|
||||
ei_current = abs(get_line_amount('EI_EMP') or get_line_amount('EI') or 0)
|
||||
cpp_current = abs(get_line_amount('CPP_EMP') or get_line_amount('CPP') or 0)
|
||||
cpp2_current = abs(get_line_amount('CPP2_EMP') or get_line_amount('CPP2') or 0)
|
||||
|
||||
# If individual line values are 0, calculate from payslip totals
|
||||
total_taxes_from_lines = income_tax_current + ei_current + cpp_current + cpp2_current
|
||||
if total_taxes_from_lines == 0 and payslip.basic_wage > 0 and payslip.net_wage > 0:
|
||||
# Calculate total taxes as difference between basic and net
|
||||
total_taxes_calculated = payslip.basic_wage - payslip.net_wage
|
||||
if total_taxes_calculated > 0:
|
||||
# Approximate breakdown based on typical Canadian tax rates
|
||||
# CPP ~5.95%, EI ~1.63%, Income Tax = remainder
|
||||
gross = payslip.basic_wage
|
||||
cpp_current = min(gross * 0.0595, 3867.50) # 2025 CPP max
|
||||
ei_current = min(gross * 0.0163, 1049.12) # 2025 EI max
|
||||
income_tax_current = max(0, total_taxes_calculated - cpp_current - ei_current)
|
||||
cpp2_current = 0 # Usually 0 unless over threshold
|
||||
|
||||
income_tax_ytd = abs(get_ytd_amount('FIT') or get_ytd_amount('INCOMETAX') or 0)
|
||||
ei_ytd = abs(get_ytd_amount('EI_EMP') or get_ytd_amount('EI') or 0)
|
||||
cpp_ytd = abs(get_ytd_amount('CPP_EMP') or get_ytd_amount('CPP') or 0)
|
||||
cpp2_ytd = abs(get_ytd_amount('CPP2_EMP') or get_ytd_amount('CPP2') or 0)
|
||||
|
||||
# Calculate totals
|
||||
total_taxes_current = income_tax_current + ei_current + cpp_current + cpp2_current
|
||||
total_taxes_ytd = income_tax_ytd + ei_ytd + cpp_ytd + cpp2_ytd
|
||||
|
||||
total_pay_current = regular_pay_current + vacation_pay_current + stat_pay_current
|
||||
total_pay_ytd = regular_pay_ytd + vacation_pay_ytd + stat_pay_ytd
|
||||
|
||||
# Get employer contributions
|
||||
employer_ei_current = abs(get_line_amount('EI_ER') or 0)
|
||||
employer_cpp_current = abs(get_line_amount('CPP_ER') or 0)
|
||||
employer_cpp2_current = abs(get_line_amount('CPP2_ER') or 0)
|
||||
|
||||
# If no employer lines, calculate from employee amounts
|
||||
if employer_ei_current == 0 and ei_current > 0:
|
||||
employer_ei_current = ei_current * 1.4 # EI employer is 1.4x employee
|
||||
if employer_cpp_current == 0 and cpp_current > 0:
|
||||
employer_cpp_current = cpp_current # CPP employer matches employee
|
||||
if employer_cpp2_current == 0 and cpp2_current > 0:
|
||||
employer_cpp2_current = cpp2_current # CPP2 employer matches employee
|
||||
|
||||
return {
|
||||
'pay': {
|
||||
'regular_pay': {
|
||||
'hours': regular_hours,
|
||||
'rate': hourly_rate,
|
||||
'current': regular_pay_current,
|
||||
'ytd': regular_pay_ytd,
|
||||
},
|
||||
'vacation_pay': {
|
||||
'hours': '-',
|
||||
'rate': '-',
|
||||
'current': vacation_pay_current,
|
||||
'ytd': vacation_pay_ytd,
|
||||
},
|
||||
'stat_holiday_pay': {
|
||||
'hours': '-',
|
||||
'rate': hourly_rate,
|
||||
'current': stat_pay_current,
|
||||
'ytd': stat_pay_ytd,
|
||||
},
|
||||
},
|
||||
'taxes': {
|
||||
'income_tax': {
|
||||
'current': income_tax_current,
|
||||
'ytd': income_tax_ytd,
|
||||
},
|
||||
'ei': {
|
||||
'current': ei_current,
|
||||
'ytd': ei_ytd,
|
||||
},
|
||||
'cpp': {
|
||||
'current': cpp_current,
|
||||
'ytd': cpp_ytd,
|
||||
},
|
||||
'cpp2': {
|
||||
'current': cpp2_current,
|
||||
'ytd': cpp2_ytd,
|
||||
},
|
||||
},
|
||||
'summary': {
|
||||
'total_pay': {
|
||||
'current': total_pay_current,
|
||||
'ytd': total_pay_ytd,
|
||||
},
|
||||
'taxes': {
|
||||
'current': total_taxes_current,
|
||||
'ytd': total_taxes_ytd,
|
||||
},
|
||||
'deductions': {
|
||||
'current': 0,
|
||||
'ytd': 0,
|
||||
},
|
||||
'net_pay': {
|
||||
'current': self.amount,
|
||||
'ytd': sum(s.net_wage for s in ytd_payslips) + self.amount,
|
||||
},
|
||||
},
|
||||
'benefits': {
|
||||
'vacation': {
|
||||
'accrued': 0,
|
||||
'used': 0,
|
||||
'available': 0,
|
||||
},
|
||||
},
|
||||
'employer': {
|
||||
'ei': {
|
||||
'current': employer_ei_current,
|
||||
},
|
||||
'cpp': {
|
||||
'current': employer_cpp_current,
|
||||
},
|
||||
'cpp2': {
|
||||
'current': employer_cpp2_current,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@api.model
|
||||
def create_from_payslip(self, payslip):
|
||||
"""Create a cheque from a payslip."""
|
||||
# Check if employee payment method is cheque
|
||||
if hasattr(payslip.employee_id, 'payment_method') and payslip.employee_id.payment_method != 'cheque':
|
||||
return False
|
||||
|
||||
# Check if cheque already exists for this payslip
|
||||
existing = self.search([('payslip_id', '=', payslip.id)], limit=1)
|
||||
if existing:
|
||||
return existing
|
||||
|
||||
cheque = self.create({
|
||||
'employee_id': payslip.employee_id.id,
|
||||
'payslip_id': payslip.id,
|
||||
'payslip_run_id': payslip.payslip_run_id.id if payslip.payslip_run_id else False,
|
||||
'amount': payslip.net_wage,
|
||||
'cheque_date': payslip.date_to,
|
||||
'pay_period_start': payslip.date_from,
|
||||
'pay_period_end': payslip.date_to,
|
||||
'company_id': payslip.company_id.id,
|
||||
})
|
||||
|
||||
# Link cheque back to payslip
|
||||
if cheque and hasattr(payslip, 'cheque_id'):
|
||||
payslip.cheque_id = cheque.id
|
||||
|
||||
return cheque
|
||||
|
||||
def action_mark_printed(self):
|
||||
"""Mark cheque as printed and assign number if not assigned."""
|
||||
for cheque in self:
|
||||
if not cheque.cheque_number:
|
||||
cheque.action_assign_number()
|
||||
cheque.state = 'printed'
|
||||
|
||||
|
||||
class PayrollChequeVoidWizard(models.TransientModel):
|
||||
"""Wizard to void a cheque with reason."""
|
||||
_name = 'payroll.cheque.void.wizard'
|
||||
_description = 'Void Cheque Wizard'
|
||||
|
||||
cheque_id = fields.Many2one('payroll.cheque', required=True)
|
||||
void_reason = fields.Text(string='Void Reason', required=True)
|
||||
|
||||
def action_void(self):
|
||||
"""Void the cheque."""
|
||||
self.cheque_id.write({
|
||||
'state': 'voided',
|
||||
'voided_date': fields.Datetime.now(),
|
||||
'void_reason': self.void_reason,
|
||||
})
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
|
||||
class PayrollChequeLayout(models.Model):
|
||||
"""
|
||||
Cheque Layout Configuration
|
||||
Allows customizing field positions on the cheque.
|
||||
"""
|
||||
_name = 'payroll.cheque.layout'
|
||||
_description = 'Cheque Layout'
|
||||
|
||||
name = fields.Char(string='Layout Name', required=True)
|
||||
active = fields.Boolean(default=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
|
||||
# Page Settings
|
||||
page_width = fields.Float(string='Page Width (inches)', default=8.5)
|
||||
page_height = fields.Float(string='Page Height (inches)', default=11)
|
||||
|
||||
# Cheque Section (Top)
|
||||
cheque_height = fields.Float(string='Cheque Height (inches)', default=3.5)
|
||||
|
||||
# Date Position
|
||||
date_x = fields.Float(string='Date X Position', default=6.5)
|
||||
date_y = fields.Float(string='Date Y Position', default=0.5)
|
||||
date_format = fields.Selection([
|
||||
('mmddyyyy', 'MMDDYYYY'),
|
||||
('mm/dd/yyyy', 'MM/DD/YYYY'),
|
||||
('yyyy-mm-dd', 'YYYY-MM-DD'),
|
||||
], string='Date Format', default='mmddyyyy')
|
||||
|
||||
# Amount Position
|
||||
amount_x = fields.Float(string='Amount X Position', default=6.5)
|
||||
amount_y = fields.Float(string='Amount Y Position', default=1.5)
|
||||
|
||||
# Amount in Words Position
|
||||
amount_words_x = fields.Float(string='Amount Words X', default=0.5)
|
||||
amount_words_y = fields.Float(string='Amount Words Y', default=1.0)
|
||||
|
||||
# Payee Position
|
||||
payee_x = fields.Float(string='Payee X Position', default=0.5)
|
||||
payee_y = fields.Float(string='Payee Y Position', default=1.5)
|
||||
|
||||
# Memo Position
|
||||
memo_x = fields.Float(string='Memo X Position', default=0.5)
|
||||
memo_y = fields.Float(string='Memo Y Position', default=2.5)
|
||||
|
||||
# Pay Period Position
|
||||
pay_period_x = fields.Float(string='Pay Period X', default=0.5)
|
||||
pay_period_y = fields.Float(string='Pay Period Y', default=2.0)
|
||||
|
||||
# Stub Settings
|
||||
stub_height = fields.Float(string='Stub Height (inches)', default=3.75)
|
||||
show_employer_copy = fields.Boolean(string='Show Employer Copy', default=True)
|
||||
|
||||
@api.model
|
||||
def get_default_layout(self):
|
||||
"""Get or create the default layout."""
|
||||
layout = self.search([
|
||||
('company_id', '=', self.env.company.id),
|
||||
], limit=1)
|
||||
|
||||
if not layout:
|
||||
layout = self.create({
|
||||
'name': 'Default Cheque Layout',
|
||||
'company_id': self.env.company.id,
|
||||
})
|
||||
|
||||
return layout
|
||||
Reference in New Issue
Block a user