Initial commit
This commit is contained in:
232
fusion_payroll/models/payroll_report_paycheque.py
Normal file
232
fusion_payroll/models/payroll_report_paycheque.py
Normal file
@@ -0,0 +1,232 @@
|
||||
# -*- 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 []
|
||||
Reference in New Issue
Block a user