Initial commit

This commit is contained in:
gsinghpal
2026-02-22 01:22:18 -05:00
commit 5200d5baf0
2394 changed files with 386834 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 969 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -0,0 +1,154 @@
@page {
size: letter;
margin: 0.45in 0.35in;
}
* {
box-sizing: border-box;
}
html, body {
font-family: Arial, sans-serif !important;
margin: 0 !important;
padding: 0 !important;
}
.page, .article, main {
margin: 0 !important;
padding: 0 !important;
}
div[class*="page"], div[class*="article"] {
margin-top: 0 !important;
margin-bottom: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.roe-wrapper {
position: relative;
width: 100%;
margin: 0;
padding: 0;
}
.roe-wrapper .watermark {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotate(-45deg);
font-size: 125px;
font-weight: bold;
color: #000;
opacity: 0.07;
white-space: nowrap;
pointer-events: none;
z-index: 10;
}
.roe-wrapper .roe-form {
border: 2px solid black !important;
position: relative;
width: 100%;
}
.roe-wrapper .roe-form .header {
background-color: #000 !important;
color: #fff !important;
padding: 5px 10px !important;
font-weight: bold !important;
font-size: 15px !important;
}
.roe-wrapper .roe-form table {
width: 100% !important;
border-collapse: collapse !important;
}
.roe-wrapper .roe-form table td {
border: 1px solid black !important;
padding: 4px !important;
vertical-align: top !important;
}
.roe-wrapper .field-label {
background-color: #666 !important;
color: #fff !important;
font-size: 10px !important;
font-weight: bold !important;
padding: 2px 4px !important;
line-height: 1.1 !important;
display: inline-block !important;
}
.roe-wrapper .field-number {
background-color: #000 !important;
color: #fff !important;
font-weight: bold !important;
padding: 2px 4px !important;
font-size: 10px !important;
display: inline-block !important;
}
.roe-wrapper .small-text {
font-size: 9px !important;
line-height: 1.1 !important;
}
.roe-wrapper .date-boxes {
display: flex !important;
flex-direction: row !important;
gap: 2px !important;
align-items: center !important;
margin-top: 3px !important;
flex-wrap: nowrap !important;
}
.roe-wrapper .date-box {
border: 1px solid black !important;
width: 20px !important;
height: 20px !important;
text-align: center !important;
font-size: 11px !important;
line-height: 20px !important;
display: inline-block !important;
flex-shrink: 0 !important;
}
.roe-wrapper .date-label {
font-size: 10px !important;
margin-right: 2px !important;
flex-shrink: 0 !important;
}
.roe-wrapper .earnings-table {
width: 100% !important;
font-size: 10px !important;
}
.roe-wrapper .earnings-table td {
text-align: center !important;
padding: 2px !important;
border: 1px solid black !important;
}
.roe-wrapper .pp-col {
width: 30px !important;
}
.roe-wrapper .footer-note {
font-size: 9px !important;
padding: 4px !important;
}
.roe-wrapper .checkbox {
display: inline-block !important;
width: 14px !important;
height: 14px !important;
border: 1px solid black !important;
margin-right: 4px !important;
text-align: center !important;
font-size: 11px !important;
line-height: 14px !important;
vertical-align: middle !important;
}

View File

@@ -0,0 +1,266 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component, useState, onWillStart } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
import { rpc } from "@web/core/network/rpc";
import { _t } from "@web/core/l10n/translation";
/**
* PayrollReportAction - Main OWL component for payroll reports
* Provides QuickBooks-style filtering, viewing, and export functionality.
*/
export class PayrollReportAction extends Component {
static template = "fusion_payroll.PayrollReportAction";
static props = {
action: { type: Object },
actionId: { type: [Number, Boolean], optional: true },
};
setup() {
this.orm = useService("orm");
this.actionService = useService("action");
this.notification = useService("notification");
// Get report model from action context
this.reportModel = this.props.action.context?.report_model || 'payroll.report.paycheque.history';
this.state = useState({
loading: true,
reportName: '',
columns: [],
lines: [],
options: {},
dateFilterOptions: [],
filterDateRange: true,
filterEmployee: true,
filterDepartment: false,
selectedDateFilter: 'this_year',
dateFrom: '',
dateTo: '',
showCustomDate: false,
sortColumn: null,
sortDirection: 'asc',
expandedLines: {},
});
onWillStart(async () => {
await this.loadReportData();
});
}
async loadReportData(options = null) {
this.state.loading = true;
try {
const result = await rpc("/payroll/report/get_report_data", {
report_model: this.reportModel,
options: options || this.state.options,
});
if (result.error) {
console.error("Report error:", result.error, result.traceback);
this.notification.add(_t("Error loading report: %s", result.error), { type: "danger" });
this.state.loading = false;
return;
}
this.state.reportName = result.report_name;
this.state.columns = result.columns || [];
this.state.lines = result.lines || [];
this.state.options = result.options || {};
this.state.dateFilterOptions = result.date_filter_options || [];
this.state.filterDateRange = result.filter_date_range !== undefined ? result.filter_date_range : true;
this.state.filterEmployee = result.filter_employee !== undefined ? result.filter_employee : true;
this.state.filterDepartment = result.filter_department !== undefined ? result.filter_department : false;
// Set date filter values
if (result.options && result.options.date) {
this.state.selectedDateFilter = result.options.date.filter;
this.state.dateFrom = result.options.date.date_from;
this.state.dateTo = result.options.date.date_to;
this.state.showCustomDate = result.options.date.filter === 'custom';
}
} catch (error) {
console.error("Error loading report:", error);
this.notification.add(_t("Error loading report: %s", error.message || error), { type: "danger" });
}
this.state.loading = false;
}
async onDateFilterChange(ev) {
const filter = ev.target.value;
this.state.selectedDateFilter = filter;
this.state.showCustomDate = filter === 'custom';
if (filter !== 'custom') {
await this.applyFilters();
}
}
async onDateFromChange(ev) {
this.state.dateFrom = ev.target.value;
}
async onDateToChange(ev) {
this.state.dateTo = ev.target.value;
}
async applyFilters() {
const options = {
...this.state.options,
date: {
filter: this.state.selectedDateFilter,
date_from: this.state.dateFrom,
date_to: this.state.dateTo,
},
};
await this.loadReportData(options);
}
sortByColumn(column) {
if (!column.sortable) return;
if (this.state.sortColumn === column.field) {
this.state.sortDirection = this.state.sortDirection === 'asc' ? 'desc' : 'asc';
} else {
this.state.sortColumn = column.field;
this.state.sortDirection = 'asc';
}
// Sort lines
const sortedLines = [...this.state.lines].sort((a, b) => {
const aVal = a.values?.[column.field] ?? '';
const bVal = b.values?.[column.field] ?? '';
// Keep totals at top/bottom
if (a.level < 0) return -1;
if (b.level < 0) return 1;
let comparison = 0;
if (typeof aVal === 'number' && typeof bVal === 'number') {
comparison = aVal - bVal;
} else {
comparison = String(aVal).localeCompare(String(bVal));
}
return this.state.sortDirection === 'asc' ? comparison : -comparison;
});
this.state.lines = sortedLines;
}
async toggleLine(lineId) {
if (this.state.expandedLines[lineId]) {
delete this.state.expandedLines[lineId];
} else {
this.state.expandedLines[lineId] = true;
// Load detail lines if needed
await this.loadDetailLines(lineId);
}
}
async loadDetailLines(lineId) {
try {
const result = await rpc("/payroll/report/get_detail_lines", {
report_model: this.reportModel,
line_id: lineId,
options: this.state.options,
});
if (result.error) {
console.error("Detail lines error:", result.error);
return;
}
if (result.detail_lines && result.detail_lines.length > 0) {
// Insert detail lines after the parent line
const lineIndex = this.state.lines.findIndex(l => l.id === lineId);
if (lineIndex >= 0) {
const newLines = [...this.state.lines];
newLines.splice(lineIndex + 1, 0, ...result.detail_lines);
this.state.lines = newLines;
}
}
} catch (error) {
console.error("Error loading detail lines:", error);
}
}
async exportXlsx() {
const options = JSON.stringify(this.state.options);
const url = `/payroll/report/export_xlsx?report_model=${this.reportModel}&options=${encodeURIComponent(options)}`;
window.location.href = url;
}
async exportPdf() {
const options = JSON.stringify(this.state.options);
const url = `/payroll/report/export_pdf?report_model=${this.reportModel}&options=${encodeURIComponent(options)}`;
window.open(url, '_blank');
}
async openRecord(line) {
if (line.model && line.res_id) {
await this.actionService.doAction({
type: 'ir.actions.act_window',
res_model: line.model,
res_id: line.res_id,
views: [[false, 'form']],
target: 'current',
});
}
}
formatValue(value, columnType) {
if (value === null || value === undefined || value === '') {
return '';
}
switch (columnType) {
case 'monetary':
return new Intl.NumberFormat('en-CA', {
style: 'currency',
currency: 'CAD',
}).format(value);
case 'float':
return new Intl.NumberFormat('en-CA', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(value);
case 'integer':
return Math.round(value).toString();
case 'date':
if (value) {
return new Date(value).toLocaleDateString('en-CA');
}
return '';
case 'percentage':
return `${(value * 100).toFixed(2)}%`;
default:
return String(value);
}
}
getLineClass(line) {
const classes = [];
if (line.class) {
classes.push(line.class);
}
if (line.level < 0) {
classes.push('o_payroll_report_total');
}
if (line.level > 0) {
classes.push('o_payroll_report_detail');
}
return classes.join(' ');
}
goBack() {
this.actionService.doAction({
type: 'ir.actions.client',
tag: 'fusion_payroll.report_hub',
});
}
}
// Register the component as a client action
registry.category("actions").add("fusion_payroll.payroll_report_action", PayrollReportAction);

View File

@@ -0,0 +1,48 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class FusionPayrollReportHub extends Component {
static template = "fusion_payroll.ReportHub";
setup() {
this.actionService = useService("action");
}
async openReport(actionXmlId) {
try {
await this.actionService.doAction(actionXmlId);
} catch (error) {
console.error("Failed to open report:", actionXmlId, error);
// Fallback: try to open directly with context
const reportModels = {
'fusion_payroll.action_payroll_report_paycheque_history': 'payroll.report.paycheque.history',
'fusion_payroll.action_payroll_report_payroll_details': 'payroll.report.payroll.details',
'fusion_payroll.action_payroll_report_tax_liability': 'payroll.report.tax.liability',
'fusion_payroll.action_payroll_report_tax_payments': 'payroll.report.tax.payments',
'fusion_payroll.action_payroll_report_tax_wage_summary': 'payroll.report.tax.wage.summary',
'fusion_payroll.action_payroll_report_summary': 'payroll.report.summary',
'fusion_payroll.action_payroll_report_summary_employee': 'payroll.report.summary.by.employee',
'fusion_payroll.action_payroll_report_employee_directory': 'payroll.report.employee.directory',
'fusion_payroll.action_payroll_report_time_off': 'payroll.report.time.off',
'fusion_payroll.action_payroll_report_total_pay': 'payroll.report.total.pay',
'fusion_payroll.action_payroll_report_total_cost': 'payroll.report.total.cost',
'fusion_payroll.action_payroll_report_deductions': 'payroll.report.deductions',
'fusion_payroll.action_payroll_report_workers_comp': 'payroll.report.workers.comp',
};
const reportModel = reportModels[actionXmlId];
if (reportModel) {
await this.actionService.doAction({
type: 'ir.actions.client',
tag: 'fusion_payroll.payroll_report_action',
name: 'Report',
context: { report_model: reportModel },
});
}
}
}
}
registry.category("actions").add("fusion_payroll.report_hub", FusionPayrollReportHub);

View File

@@ -0,0 +1,96 @@
/* Payroll Report Styles */
.o_payroll_report_action {
min-height: 100vh;
.o_payroll_report_table {
thead {
position: sticky;
top: 0;
z-index: 1;
th {
white-space: nowrap;
font-weight: 600;
border-bottom: 2px solid;
}
}
tbody {
tr {
transition: background-color 0.15s ease-in-out;
&.o_payroll_report_total {
font-weight: 600;
border-top: 2px solid;
}
&.o_payroll_report_detail {
font-size: 0.9em;
td:first-child {
padding-left: 2rem;
}
}
}
td {
vertical-align: middle;
white-space: nowrap;
&.text-end {
font-family: 'Roboto Mono', monospace;
}
}
}
}
/* Filter Card - Let Bootstrap handle background */
.card {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
/* Date Inputs */
.form-control-sm {
height: 32px;
}
.form-select-sm {
height: 32px;
}
}
/* Report Hub Styles */
.o_fusion_report_hub {
.card {
transition: transform 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.card-body {
padding: 0.75rem 1rem;
}
}
}
/* Print Styles */
@media print {
.o_payroll_report_action {
background-color: white;
.btn, .form-control, .form-select {
display: none;
}
.o_payroll_report_table {
font-size: 10pt;
th, td {
padding: 4px 8px;
}
}
}
}

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="fusion_payroll.PayrollReportAction">
<div class="o_action o_payroll_report_action">
<!-- Loading Spinner -->
<div t-if="state.loading" class="d-flex justify-content-center align-items-center" style="min-height: 400px;">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div t-else="" class="container-fluid py-3">
<!-- Header -->
<div class="row mb-3">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center">
<button class="btn btn-link p-0 me-3" t-on-click="goBack">
<i class="fa fa-chevron-left"/> Back
</button>
<h2 class="mb-0">
<t t-out="state.reportName"/>
</h2>
</div>
<div class="d-flex gap-2">
<!-- Export Buttons -->
<div class="btn-group">
<button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown">
Export <i class="fa fa-caret-down ms-1"/>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="#" t-on-click.prevent="exportXlsx">
<i class="fa fa-file-excel-o me-2"/> Export to Excel
</a>
</li>
<li>
<a class="dropdown-item" href="#" t-on-click.prevent="exportPdf">
<i class="fa fa-file-pdf-o me-2"/> Export to PDF
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Filters Row -->
<div class="row mb-3" t-if="state.filterDateRange">
<div class="col-12">
<div class="card">
<div class="card-body py-2">
<div class="row align-items-center">
<!-- Date Filter Dropdown -->
<div class="col-auto">
<select class="form-select form-select-sm"
t-on-change="onDateFilterChange"
t-att-value="state.selectedDateFilter">
<t t-foreach="state.dateFilterOptions" t-as="option" t-key="option[0]">
<option t-att-value="option[0]"
t-att-selected="option[0] === state.selectedDateFilter">
<t t-out="option[1]"/>
</option>
</t>
</select>
</div>
<!-- Date From -->
<div class="col-auto">
<input type="date"
class="form-control form-control-sm"
t-att-value="state.dateFrom"
t-on-change="onDateFromChange"/>
</div>
<!-- Date To -->
<div class="col-auto">
<input type="date"
class="form-control form-control-sm"
t-att-value="state.dateTo"
t-on-change="onDateToChange"/>
</div>
<!-- Apply Button (for custom dates) -->
<div class="col-auto" t-if="state.showCustomDate">
<button class="btn btn-primary btn-sm" t-on-click="applyFilters">
Apply
</button>
</div>
<!-- Customize Button -->
<div class="col-auto ms-auto">
<button class="btn btn-outline-secondary btn-sm">
<i class="fa fa-sliders me-1"/> Customize
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Report Table -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover table-sm mb-0 o_payroll_report_table">
<thead>
<tr>
<t t-foreach="state.columns" t-as="column" t-key="column.field">
<th t-att-class="column.sortable ? 'cursor-pointer user-select-none' : ''"
t-on-click="() => this.sortByColumn(column)">
<t t-out="column.name"/>
<t t-if="column.sortable">
<i t-if="state.sortColumn === column.field"
t-att-class="'fa ms-1 ' + (state.sortDirection === 'asc' ? 'fa-sort-asc' : 'fa-sort-desc')"/>
<i t-else="" class="fa fa-sort ms-1 text-muted"/>
</t>
</th>
</t>
</tr>
</thead>
<tbody>
<t t-if="state.lines.length === 0">
<tr>
<td t-att-colspan="state.columns.length" class="text-center text-muted py-4">
There are no results matching the criteria.
</td>
</tr>
</t>
<t t-foreach="state.lines" t-as="line" t-key="line.id">
<tr t-att-class="this.getLineClass(line)"
t-on-click="() => line.model and line.res_id and this.openRecord(line)"
style="cursor: pointer;">
<t t-foreach="state.columns" t-as="column" t-key="column.field">
<td t-att-class="column.type === 'monetary' or column.type === 'float' ? 'text-end' : ''">
<!-- Unfoldable indicator -->
<t t-if="column_index === 0 and line.unfoldable">
<i t-att-class="'fa me-1 ' + (state.expandedLines[line.id] ? 'fa-caret-down' : 'fa-caret-right')"
t-on-click.stop="() => this.toggleLine(line.id)"/>
</t>
<t t-out="this.formatValue(line.values?.[column.field], column.type)"/>
</td>
</t>
</tr>
</t>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>

View File

@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="fusion_payroll.ReportHub">
<div class="o_action o_fusion_report_hub">
<div class="container-fluid py-4">
<!-- Header -->
<div class="row mb-4">
<div class="col-12">
<h1 class="mb-1">
<i class="fa fa-bar-chart me-2 text-primary"/>Payroll Reports
</h1>
<p class="text-muted mb-0">Select a report to view payroll data and analytics</p>
</div>
</div>
<!-- Reports Grid -->
<div class="row g-3">
<!-- Left Column -->
<div class="col-md-6">
<!-- Paycheque History -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_paycheque_history')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-file-text-o fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Paycheque History</h6>
<small class="text-muted">View all paycheques by date</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Deductions and Contributions -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_deductions')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-minus-circle fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Deductions and Contributions</h6>
<small class="text-muted">Employee and company deductions</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Payroll Summary by Employee -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_summary_employee')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-users fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Summary by Employee</h6>
<small class="text-muted">Pivot by employee</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Payroll Item List -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_employee_directory')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-address-book fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Item List</h6>
<small class="text-muted">Employee pay rates and status</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Payroll Details -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_payroll_details')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-list-alt fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Details</h6>
<small class="text-muted">Detailed breakdown per pay period</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Payroll Summary -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_summary')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-table fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Summary</h6>
<small class="text-muted">Summary with all components</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Time Off -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_time_off')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-calendar-check-o fa-lg text-primary me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Time Off</h6>
<small class="text-muted">Vacation and leave balances</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
</div>
<!-- Right Column -->
<div class="col-md-6">
<!-- Payroll Tax Liability -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_tax_liability')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-balance-scale fa-lg text-success me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Tax Liability</h6>
<small class="text-muted">Tax amounts owed vs paid</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Payroll Tax Payments -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_tax_payments')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-credit-card fa-lg text-success me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Tax Payments</h6>
<small class="text-muted">History of remittance payments</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Total Payroll Cost -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_total_cost')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-calculator fa-lg text-success me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Total Payroll Cost</h6>
<small class="text-muted">All payroll costs breakdown</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Total Pay -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_total_pay')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-money fa-lg text-success me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Total Pay</h6>
<small class="text-muted">Pay by type per employee</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Payroll Tax and Wage Summary -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_tax_wage_summary')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-pie-chart fa-lg text-success me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Payroll Tax and Wage Summary</h6>
<small class="text-muted">Wages and tax amounts</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
<!-- Workers' Compensation -->
<div class="card mb-2" style="cursor: pointer;" t-on-click="() => this.openReport('fusion_payroll.action_payroll_report_workers_comp')">
<div class="card-body py-2 d-flex align-items-center">
<i class="fa fa-shield fa-lg text-success me-3"/>
<div class="flex-grow-1">
<h6 class="mb-0">Workers' Compensation</h6>
<small class="text-muted">WCB wages by province</small>
</div>
<i class="fa fa-chevron-right text-muted"/>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</templates>