Files
Odoo-Modules/fusion_payroll/models/payroll_report_paycheque.py
2026-02-22 01:22:18 -05:00

233 lines
8.9 KiB
Python

# -*- coding: utf-8 -*-
"""
Paycheque Reports
=================
- Paycheque History
- Payroll Details
"""
from odoo import api, fields, models, _
class PayrollReportPaychequeHistory(models.AbstractModel):
"""
Paycheque History Report
Shows all paycheques with pay date, employee, amounts, payment method.
"""
_name = 'payroll.report.paycheque.history'
_inherit = 'payroll.report'
_description = 'Paycheque History Report'
report_name = 'Paycheque History'
report_code = 'paycheque_history'
def _get_columns(self):
return [
{'name': _('Pay Date'), 'field': 'pay_date', 'type': 'date', 'sortable': True},
{'name': _('Name'), 'field': 'name', 'type': 'char', 'sortable': True},
{'name': _('Total Pay'), 'field': 'total_pay', 'type': 'monetary', 'sortable': True},
{'name': _('Net Pay'), 'field': 'net_pay', 'type': 'monetary', 'sortable': True},
{'name': _('Pay Method'), 'field': 'pay_method', 'type': 'char', 'sortable': True},
{'name': _('Cheque Number'), 'field': 'cheque_number', 'type': 'char', 'sortable': False},
{'name': _('Status'), 'field': 'status', 'type': 'char', 'sortable': True},
]
def _get_lines(self, options):
domain = self._get_domain(options)
domain.append(('state', 'in', ['done', 'paid']))
payslips = self.env['hr.payslip'].search(domain, order='date_to desc, employee_id')
lines = []
for slip in payslips:
if not slip.employee_id:
continue
pay_method = '-'
if hasattr(slip, 'paid_by') and slip.paid_by:
if 'paid_by' in slip._fields and hasattr(slip._fields['paid_by'], 'selection'):
pay_method = dict(slip._fields['paid_by'].selection).get(slip.paid_by, '-')
status = slip.state
if 'state' in slip._fields and hasattr(slip._fields['state'], 'selection'):
status = dict(slip._fields['state'].selection).get(slip.state, slip.state)
lines.append({
'id': f'payslip_{slip.id}',
'name': slip.employee_id.name or 'Unknown',
'values': {
'pay_date': slip.date_to or '',
'name': slip.employee_id.name or 'Unknown',
'total_pay': slip.gross_wage or 0,
'net_pay': slip.net_wage or 0,
'pay_method': pay_method,
'cheque_number': getattr(slip, 'cheque_number', None) or '-',
'status': status,
},
'level': 0,
'model': 'hr.payslip',
'res_id': slip.id,
})
# Add total
if lines:
lines.append(self._get_total_line(lines, options))
return lines
class PayrollReportPayrollDetails(models.AbstractModel):
"""
Payroll Details Report
Detailed breakdown per employee per pay date with Gross, Taxes, Net.
"""
_name = 'payroll.report.payroll.details'
_inherit = 'payroll.report'
_description = 'Payroll Details Report'
report_name = 'Payroll Details'
report_code = 'payroll_details'
def _get_columns(self):
return [
{'name': _('Pay Date'), 'field': 'pay_date', 'type': 'date', 'sortable': True},
{'name': _('Name'), 'field': 'name', 'type': 'char', 'sortable': True},
{'name': _('Hours'), 'field': 'hours', 'type': 'float', 'sortable': True},
{'name': _('Gross Pay'), 'field': 'gross_pay', 'type': 'monetary', 'sortable': True},
{'name': _('Other Pay'), 'field': 'other_pay', 'type': 'monetary', 'sortable': False},
{'name': _('Employee Taxes'), 'field': 'employee_taxes', 'type': 'monetary', 'sortable': True},
{'name': _('Net Pay'), 'field': 'net_pay', 'type': 'monetary', 'sortable': True},
]
def _get_lines(self, options):
domain = self._get_domain(options)
domain.append(('state', 'in', ['done', 'paid']))
payslips = self.env['hr.payslip'].search(domain, order='date_to desc, employee_id')
lines = []
# Group by pay date for totals
current_date = None
date_totals = {'gross_pay': 0, 'employee_taxes': 0, 'net_pay': 0, 'hours': 0}
# Calculate grand totals
grand_totals = {'gross_pay': 0, 'employee_taxes': 0, 'net_pay': 0, 'hours': 0}
for slip in payslips:
if not slip.employee_id:
continue
# Calculate worked hours
hours = 0
if hasattr(slip, 'worked_days_line_ids') and slip.worked_days_line_ids:
hours = sum(slip.worked_days_line_ids.mapped('number_of_hours')) or 0
# Calculate employee taxes
employee_taxes = getattr(slip, 'total_employee_deductions', 0) or 0
gross_wage = getattr(slip, 'gross_wage', 0) or 0
net_wage = getattr(slip, 'net_wage', 0) or 0
grand_totals['gross_pay'] += gross_wage
grand_totals['employee_taxes'] += employee_taxes
grand_totals['net_pay'] += net_wage
grand_totals['hours'] += hours
lines.append({
'id': f'detail_{slip.id}',
'name': slip.employee_id.name or 'Unknown',
'values': {
'pay_date': slip.date_to or '',
'name': slip.employee_id.name or 'Unknown',
'hours': hours,
'gross_pay': gross_wage,
'other_pay': 0, # Can be calculated from specific line types
'employee_taxes': employee_taxes,
'net_pay': net_wage,
},
'level': 0,
'model': 'hr.payslip',
'res_id': slip.id,
'unfoldable': True, # Can expand to show breakdown
})
# Add grand total
if lines:
lines.append({
'id': 'grand_total',
'name': _('Total'),
'values': {
'pay_date': '',
'name': _('Total'),
'hours': grand_totals['hours'],
'gross_pay': grand_totals['gross_pay'],
'other_pay': 0,
'employee_taxes': grand_totals['employee_taxes'],
'net_pay': grand_totals['net_pay'],
},
'level': -1,
'class': 'o_payroll_report_total fw-bold',
})
return lines
def _get_detail_lines(self, payslip_id, options):
"""Get expanded detail lines for a payslip."""
try:
payslip = self.env['hr.payslip'].browse(payslip_id)
if not payslip.exists():
return []
lines = []
if not hasattr(payslip, 'line_ids') or not payslip.line_ids:
return lines
# Gross breakdown
gross_lines = payslip.line_ids.filtered(
lambda l: hasattr(l, 'category_id') and l.category_id and
hasattr(l.category_id, 'code') and
l.category_id.code in ['BASIC', 'ALW', 'GROSS']
)
for line in gross_lines:
lines.append({
'id': f'line_{line.id}',
'name': line.name or '',
'values': {
'pay_date': '',
'name': f' {line.name or ""}',
'hours': line.quantity if hasattr(line, 'quantity') and line.quantity else '',
'gross_pay': line.total or 0,
'other_pay': '',
'employee_taxes': '',
'net_pay': '',
},
'level': 1,
'class': 'text-muted',
})
# Tax breakdown
tax_lines = payslip.line_ids.filtered(
lambda l: hasattr(l, 'code') and l.code in ['CPP', 'CPP2', 'EI', 'FED_TAX', 'PROV_TAX']
)
for line in tax_lines:
lines.append({
'id': f'tax_{line.id}',
'name': line.name or '',
'values': {
'pay_date': '',
'name': f' {line.name or ""}',
'hours': '',
'gross_pay': '',
'other_pay': '',
'employee_taxes': abs(line.total or 0),
'net_pay': '',
},
'level': 1,
'class': 'text-muted',
})
return lines
except Exception:
return []