This commit is contained in:
esam 2025-12-11 11:07:17 -05:00
parent 5e8f8b7f2b
commit 8f981a34ed
11 changed files with 944 additions and 0 deletions

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizard
from . import report

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
{
'name': 'HR Payroll Al-Bilad Bank',
'version': '1.0',
'category': 'Odex30-HR/Odex30-HR',
'sequence': 5,
'website': 'http://exp-sa.com',
'license': 'GPL-3',
'author': 'Expert Co. Ltd.',
'summary': 'Al-Bilad Bank Payroll Report Extension',
'description': """
Extends payroll module to add Al-Bilad Bank report format.
""",
'depends': [
'exp_payroll_custom',
],
'data': [
'security/ir.model.access.csv',
'wizard/payroll_bank_report_view.xml',
'report/payroll_bank_report_views.xml',
'report/payroll_bank_albilad_pdf_template.xml',
],
'installable': True,
'auto_install': False,
'application': False,
}

View File

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import payroll_bank_albilad_report

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="albilad_bank_pdf">
<t t-call="exp_payroll_albilad.external_layout_albilad">
<main>
<div class="page" dir="rtl">
<style>
.albilad-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
font-size: 10pt;
}
.albilad-table th, .albilad-table td {
border: 1px solid #000;
padding: 6px 4px;
text-align: center;
vertical-align: middle;
}
.albilad-header {
background-color: #3e5d7f;
color: #ffffff;
font-weight: bold;
font-size: 10pt;
}
.albilad-bank-header {
background-color: #FFA500;
font-weight: bold;
font-size: 13pt;
text-align: center;
padding: 10px;
margin-top: 25px;
margin-bottom: 10px;
}
.albilad-title {
background-color: #3e5d7f;
color: #ffffff;
font-weight: bold;
font-size: 12pt;
text-align: center;
padding: 12px;
margin-bottom: 20px;
}
.albilad-data-row {
font-size: 9pt;
}
</style>
<div class="albilad-title">
<t t-esc="report_title"/> <t t-esc="date_from"/> - <t t-esc="date_to"/>
</div>
<t t-if="no_details">
<t t-foreach="payslip_data" t-as="bank_data">
<div class="albilad-bank-header">
<t t-esc="bank_data.get('bank_name', '')"/>
</div>
<table class="albilad-table">
<thead>
<tr class="albilad-header">
<th>المبلغ الاجمالي</th>
<th>رقم الايبان</th>
<th>اسم المستفيد</th>
<th>بنك المستفيد</th>
<th>تفاصيل التحويل</th>
<th>الراتب الاساسي</th>
<th>بدل السكن</th>
<th>البدلات الاخرى</th>
<th>الخصم</th>
<th>رقم الهوية</th>
</tr>
</thead>
<tbody>
<t t-foreach="bank_data.get('payslips', [])" t-as="payslip">
<tr class="albilad-data-row">
<td><t t-esc="payslip.get('total_amount', 0.0)"/></td>
<td><t t-esc="payslip.get('iban', '')"/></td>
<td><t t-esc="payslip.get('name', '')"/></td>
<td><t t-esc="payslip.get('bank_code', '')"/></td>
<td><t t-esc="payslip.get('description', '')"/></td>
<td><t t-esc="payslip.get('basic_salary', 0.0)"/></td>
<td><t t-esc="payslip.get('housing', 0.0)"/></td>
<td><t t-esc="payslip.get('other', 0.0)"/></td>
<td><t t-esc="payslip.get('deductions', 0.0)"/></td>
<td><t t-esc="payslip.get('national_id', '')"/></td>
</tr>
</t>
</tbody>
</table>
</t>
</t>
<t t-if="not no_details">
<table class="albilad-table">
<thead>
<!-- Arabic headers -->
<tr class="albilad-header">
<th>المبلغ الاجمالي</th>
<th>رقم الايبان</th>
<th>اسم المستفيد</th>
<th>بنك المستفيد</th>
<th>تفاصيل التحويل</th>
<th>الراتب الاساسي</th>
<th>بدل السكن</th>
<th>البدلات الاخرى</th>
<th>الخصم</th>
<th>رقم الهوية</th>
</tr>
</thead>
<tbody>
<t t-foreach="payslip_data" t-as="payslip">
<tr class="albilad-data-row">
<td><t t-esc="payslip.get('total_amount', 0.0)"/></td>
<td><t t-esc="payslip.get('iban', '')"/></td>
<td><t t-esc="payslip.get('name', '')"/></td>
<td><t t-esc="payslip.get('bank_code', '')"/></td>
<td><t t-esc="payslip.get('description', '')"/></td>
<td><t t-esc="payslip.get('basic_salary', 0.0)"/></td>
<td><t t-esc="payslip.get('housing', 0.0)"/></td>
<td><t t-esc="payslip.get('other', 0.0)"/></td>
<td><t t-esc="payslip.get('deductions', 0.0)"/></td>
<td><t t-esc="payslip.get('national_id', '')"/></td>
</tr>
</t>
</tbody>
</table>
</t>
</div>
</main>
</t>
</template>
<template id="external_layout_albilad" inherit_id="web.external_layout_standard">
<xpath expr="//img[@t-if='company.header']" position="replace">
</xpath>
<xpath expr="//img[@t-if='company.footer']" position="replace">
</xpath>
</template>
<record id="report_payroll_bank_albilad_pdf" model="ir.actions.report">
<field name="name">Al-Bilad Bank Payroll Report (PDF)</field>
<field name="model">payroll.bank.wiz</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">exp_payroll_albilad.albilad_bank_pdf</field>
<field name="report_file">exp_payroll_albilad.albilad_bank_pdf</field>
<field name="print_report_name">'Al-Bilad Bank Report - %s' % (object.date_from)</field>
<field name="binding_model_id" ref="exp_payroll_custom.model_payroll_bank_wiz"/>
<field name="binding_type">report</field>
</record>
</odoo>

View File

@ -0,0 +1,631 @@
# -*- coding: utf-8 -*-
from odoo import models, api
class PayrollBankAlbiladReportXlsx(models.AbstractModel):
_name = "report.exp_payroll_albilad.payroll_bank_albilad_xlsx"
_inherit = 'report.report_xlsx.abstract'
_description = 'Al-Bilad Bank Payroll Report XLSX'
@api.model
def generate_xlsx_report(self, workbook, data, payslips):
emp_ids = data['employees']
bank_ids = data['banks']
salary_ids = data['salary']
date_from = data['date_from']
date_to = data['date_to']
no_details = data['no_details']
report_type = data['report_type']
entry_type = data['entry_type']
bank_type = data['bank_type']
company_id = self.sudo().env['res.company'].browse(data['company_id'])
employees = self.sudo().env['hr.employee'].search([('id', 'in', emp_ids)])
banks = self.sudo().env['res.bank'].search([('id', 'in', bank_ids)])
salary = self.sudo().env['hr.payroll.structure'].search([('id', 'in', salary_ids)])
# Get branch setting
branch_id = self.env['ir.config_parameter'].sudo().get_param('exp_payroll_custom.branch')
branch = True if branch_id == 'True' else False
sheet = workbook.add_worksheet('Al-Bilad Bank Report')
# Define formats
format1 = workbook.add_format({
'bottom': True, 'right': True, 'left': True, 'top': True,
'align': 'center', 'valign': 'vcenter'
})
format2 = workbook.add_format({
'font_size': 11, 'bottom': True, 'right': True, 'left': True, 'top': True,
'align': 'center', 'bold': True, 'valign': 'vcenter'
})
format3 = workbook.add_format({
'font_size': 14, 'bottom': True, 'right': True, 'left': True, 'top': True,
'align': 'center', 'bold': True, 'valign': 'vcenter',
'bg_color': '#FFA500'
})
format4 = workbook.add_format({
'font_size': 11, 'bottom': True, 'right': True, 'left': True, 'top': True,
'align': 'center', 'bold': True, 'valign': 'vcenter'
})
# Set column widths
sheet.set_column('A:J', 20)
row = 0
if no_details:
# With details section
for bank in banks:
row += 2
sheet.write(row, 0, bank.name, format3)
row += 1
sheet.write(row, 0, 'Total Amount', format2)
sheet.write(row, 1, 'Beneficiary Account IBAN', format2)
sheet.write(row, 2, 'Beneficiary Name', format2)
sheet.write(row, 3, 'Beneficiary Bank CODE', format2)
sheet.write(row, 4, 'Payment Description', format2)
sheet.write(row, 5, 'Basic Salary', format2)
sheet.write(row, 6, 'Housing Allowance', format2)
sheet.write(row, 7, 'Other Earnings', format2)
sheet.write(row, 8, 'Deductions', format2)
sheet.write(row, 9, 'Beneficiary National/IqamaID', format2)
row += 1
sheet.write(row, 0, 'المبلغ الاجمالي', format2)
sheet.write(row, 1, 'رقم الايبان', format2)
sheet.write(row, 2, 'اسم المستفيد', format2)
sheet.write(row, 3, 'بنك المستفيد', format2)
sheet.write(row, 4, 'تفاصيل التحويل', format2)
sheet.write(row, 5, 'الراتب الاساسي', format2)
sheet.write(row, 6, 'بدل السكن', format2)
sheet.write(row, 7, 'البدلات الاخرى', format2)
sheet.write(row, 8, 'الخصم', format2)
sheet.write(row, 9, 'رقم الهوية', format2)
if report_type == 'salary':
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
# Get salary rules
salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted(
key=lambda v: v.sequence).ids
payslip_line_obj = self.sudo().env['hr.payslip.line']
for payslip in payslip_ids:
basic = 0.0
housing = 0.0
payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)])
if not payslip_lines_ids:
continue
for payslip_line_rec in payslip_lines_ids:
if payslip_line_rec.salary_rule_id.id in salary_rules:
if payslip_line_rec.salary_rule_id.rules_type == 'salary':
basic += payslip_line_rec.total
elif payslip_line_rec.salary_rule_id.rules_type == 'house':
housing += payslip_line_rec.total
other = round((payslip.total_allowances - basic - housing), 2)
data_list = [
payslip.employee_id.emp_no,
payslip.employee_id.name or ' ',
payslip.employee_id.bank_account_id.acc_number or ' ',
payslip.employee_id.bank_account_id.bank_id.bic,
payslip.total_sum,
payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else payslip.employee_id.iqama_number.iqama_id,
basic, housing, other, round((payslip.total_deductions + payslip.total_loans), 2),
payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name,
company_id.currency_id.name, 'Active'
]
row += 1
# Write AlBilad format data
sheet.write(row, 0, data_list[4], format1)
sheet.write(row, 1, data_list[2], format1)
sheet.write(row, 2, data_list[1], format1)
sheet.write(row, 3, data_list[3], format1)
sheet.write(row, 4, '', format1)
sheet.write(row, 5, data_list[6], format1)
sheet.write(row, 6, data_list[7], format1)
sheet.write(row, 7, data_list[8], format1)
sheet.write(row, 8, data_list[9], format1)
sheet.write(row, 9, data_list[5], format1)
elif report_type == 'allowance':
row = self._write_allowance_data(sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1)
elif report_type == 'overtime':
row = self._write_overtime_data(sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1)
elif report_type == 'mission':
row = self._write_mission_data(sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1)
elif report_type == 'training':
row = self._write_training_data(sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1)
else:
# No details section
row = 1
# English headers row
sheet.write(0, 0, 'Total Amount', format2)
sheet.write(0, 1, 'Beneficiary Account IBAN', format2)
sheet.write(0, 2, 'Beneficiary Name', format2)
sheet.write(0, 3, 'Beneficiary Bank CODE', format2)
sheet.write(0, 4, 'Payment Description', format2)
sheet.write(0, 5, 'Basic Salary', format2)
sheet.write(0, 6, 'Housing Allowance', format2)
sheet.write(0, 7, 'Other Earnings', format2)
sheet.write(0, 8, 'Deductions', format2)
sheet.write(0, 9, 'Beneficiary National/IqamaID', format2)
# Arabic headers row
sheet.write(1, 0, 'المبلغ الاجمالي', format2)
sheet.write(1, 1, 'رقم الايبان', format2)
sheet.write(1, 2, 'اسم المستفيد', format2)
sheet.write(1, 3, 'بنك المستفيد', format2)
sheet.write(1, 4, 'تفاصيل التحويل', format2)
sheet.write(1, 5, 'الراتب الاساسي', format2)
sheet.write(1, 6, 'بدل السكن', format2)
sheet.write(1, 7, 'البدلات الاخرى', format2)
sheet.write(1, 8, 'الخصم', format2)
sheet.write(1, 9, 'رقم الهوية', format2)
if report_type == 'salary':
for bank in banks:
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
salary_rules = self.sudo().env['hr.salary.rule'].search([]).sorted(
key=lambda v: v.sequence).ids
payslip_line_obj = self.sudo().env['hr.payslip.line']
for payslip in payslip_ids:
basic = 0.0
housing = 0.0
payslip_lines_ids = payslip_line_obj.sudo().search([('slip_id', '=', payslip.id)])
if not payslip_lines_ids:
continue
for payslip_line_rec in payslip_lines_ids:
if payslip_line_rec.salary_rule_id.id in salary_rules:
if payslip_line_rec.salary_rule_id.rules_type == 'salary':
basic += payslip_line_rec.total
elif payslip_line_rec.salary_rule_id.rules_type == 'house':
housing += payslip_line_rec.total
other = round((payslip.total_allowances - basic - housing), 2)
data_list = [
payslip.employee_id.emp_no,
payslip.employee_id.name or ' ',
payslip.employee_id.bank_account_id.acc_number or ' ',
payslip.employee_id.bank_account_id.bank_id.bic,
payslip.total_sum,
payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality == True else payslip.employee_id.iqama_number.iqama_id,
basic, housing, other, round((payslip.total_deductions + payslip.total_loans), 2),
payslip.employee_id.branch_id.name if branch else payslip.employee_id.working_location.name,
company_id.currency_id.name, 'Active'
]
row += 1
sheet.write(row, 0, data_list[4], format1)
sheet.write(row, 1, data_list[2], format1)
sheet.write(row, 2, data_list[1], format1)
sheet.write(row, 3, data_list[3], format1)
sheet.write(row, 4, '', format1)
sheet.write(row, 5, data_list[6], format1)
sheet.write(row, 6, data_list[7], format1)
sheet.write(row, 7, data_list[8], format1)
sheet.write(row, 8, data_list[9], format1)
sheet.write(row, 9, data_list[5], format1)
def _get_payslips(self, date_from, date_to, entry_type, employees, salary, salary_ids, bank):
domain_base = [
('date_from', '>=', date_from),
('date_to', '<=', date_to),
('payslip_run_id.state', 'in', ['confirmed', 'transfered']),
('employee_id.bank_account_id.bank_id', '=', bank.id)
]
if entry_type == 'all':
domain = domain_base.copy()
if employees:
domain.append(('employee_id', 'in', employees.ids))
elif salary:
domain.append(('struct_id', 'in', salary_ids))
elif entry_type == 'posted':
domain = domain_base.copy()
if employees:
domain.extend([
('employee_id', 'in', employees.ids),
'|',
('move_id.state', '=', 'posted'),
('payslip_run_id.move_id.state', '=', 'posted')
])
elif salary:
domain.extend([
('struct_id', 'in', salary_ids),
'|',
('move_id.state', '=', 'posted'),
('payslip_run_id.move_id.state', '=', 'posted')
])
elif entry_type == 'unposted':
domain = domain_base.copy()
if employees:
domain.extend([
('employee_id', 'in', employees.ids),
('move_id.state', '!=', 'posted'),
('payslip_run_id.move_id.state', '!=', 'posted')
])
elif salary:
domain.extend([
('struct_id', 'in', salary_ids),
('move_id.state', '!=', 'posted'),
('payslip_run_id.move_id.state', '!=', 'posted')
])
else:
domain = domain_base
return self.sudo().env['hr.payslip'].search(domain)
def _write_allowance_data(self, sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1):
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
for payslip in payslip_ids:
data_list = [
payslip.employee_id.emp_no,
payslip.employee_id.name or ' ',
payslip.employee_id.bank_account_id.acc_number or ' ',
payslip.employee_id.bank_account_id.bank_id.bic,
payslip.total_sum,
payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality else payslip.employee_id.iqama_number.iqama_id,
payslip.total_allowances, 0.0, 0.0, round((payslip.total_deductions + payslip.total_loans), 2),
]
row += 1
sheet.write(row, 0, data_list[4], format1)
sheet.write(row, 1, data_list[2], format1)
sheet.write(row, 2, data_list[1], format1)
sheet.write(row, 3, data_list[3], format1)
sheet.write(row, 4, '', format1)
sheet.write(row, 5, data_list[6], format1)
sheet.write(row, 6, data_list[7], format1)
sheet.write(row, 7, data_list[8], format1)
sheet.write(row, 8, data_list[9], format1)
sheet.write(row, 9, data_list[5], format1)
return row
def _write_overtime_data(self, sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1):
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
for payslip in payslip_ids:
data_list = [
payslip.employee_id.emp_no,
payslip.employee_id.name or ' ',
payslip.employee_id.bank_account_id.acc_number or ' ',
payslip.employee_id.bank_account_id.bank_id.bic,
payslip.total_sum,
payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality else payslip.employee_id.iqama_number.iqama_id,
0.0, 0.0, payslip.total_allowances, round((payslip.total_deductions + payslip.total_loans), 2),
]
row += 1
sheet.write(row, 0, data_list[4], format1)
sheet.write(row, 1, data_list[2], format1)
sheet.write(row, 2, data_list[1], format1)
sheet.write(row, 3, data_list[3], format1)
sheet.write(row, 4, '', format1)
sheet.write(row, 5, data_list[6], format1)
sheet.write(row, 6, data_list[7], format1)
sheet.write(row, 7, data_list[8], format1)
sheet.write(row, 8, data_list[9], format1)
sheet.write(row, 9, data_list[5], format1)
return row
def _write_mission_data(self, sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1):
"""Write mission data"""
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
for payslip in payslip_ids:
# For missions, show in "Other Earnings" column
data_list = [
payslip.employee_id.emp_no,
payslip.employee_id.name or ' ',
payslip.employee_id.bank_account_id.acc_number or ' ',
payslip.employee_id.bank_account_id.bank_id.bic,
payslip.total_sum,
payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality else payslip.employee_id.iqama_number.iqama_id,
0.0, 0.0, payslip.total_allowances, round((payslip.total_deductions + payslip.total_loans), 2),
]
row += 1
sheet.write(row, 0, data_list[4], format1)
sheet.write(row, 1, data_list[2], format1)
sheet.write(row, 2, data_list[1], format1)
sheet.write(row, 3, data_list[3], format1)
sheet.write(row, 4, '', format1)
sheet.write(row, 5, data_list[6], format1)
sheet.write(row, 6, data_list[7], format1)
sheet.write(row, 7, data_list[8], format1)
sheet.write(row, 8, data_list[9], format1)
sheet.write(row, 9, data_list[5], format1)
return row
def _write_training_data(self, sheet, row, date_from, date_to, entry_type, employees, salary, salary_ids, bank, branch, company_id, format1):
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
for payslip in payslip_ids:
data_list = [
payslip.employee_id.emp_no,
payslip.employee_id.name or ' ',
payslip.employee_id.bank_account_id.acc_number or ' ',
payslip.employee_id.bank_account_id.bank_id.bic,
payslip.total_sum,
payslip.employee_id.saudi_number.saudi_id if payslip.employee_id.check_nationality else payslip.employee_id.iqama_number.iqama_id,
0.0, 0.0, payslip.total_allowances, round((payslip.total_deductions + payslip.total_loans), 2),
]
row += 1
sheet.write(row, 0, data_list[4], format1)
sheet.write(row, 1, data_list[2], format1)
sheet.write(row, 2, data_list[1], format1)
sheet.write(row, 3, data_list[3], format1)
sheet.write(row, 4, '', format1)
sheet.write(row, 5, data_list[6], format1)
sheet.write(row, 6, data_list[7], format1)
sheet.write(row, 7, data_list[8], format1)
sheet.write(row, 8, data_list[9], format1)
sheet.write(row, 9, data_list[5], format1)
return row
class HrPayslipExtended(models.Model):
_inherit = 'hr.payslip'
@api.model
def search(self, args, offset=0, limit=None, order=None):
if self.env.context.get('use_payslip_run_confirmed_state'):
modified_args = []
for item in args:
if isinstance(item, (tuple, list)) and len(item) == 3:
field, operator, value = item
if field == 'state' and operator == '=' and value == 'transfered':
modified_args.append(('payslip_run_id.state', '=', 'confirmed'))
else:
modified_args.append(item)
else:
modified_args.append(item)
args = modified_args
return super(HrPayslipExtended, self).search(args, offset, limit, order)
class PayrollXlsxExtended(models.AbstractModel):
_inherit = 'report.exp_payroll_custom.report_payroll_bank_xlsx'
@api.model
def generate_xlsx_report(self, workbook, data, payslips):
return super(PayrollXlsxExtended, self.with_context(use_payslip_run_confirmed_state=True)).generate_xlsx_report(workbook, data, payslips)
class PayrollBankAlbiladReportPdf(models.AbstractModel):
_name = "report.exp_payroll_albilad.albilad_bank_pdf"
_description = 'Al-Bilad Bank Payroll Report PDF'
@api.model
def _get_report_values(self, docids, data=None):
if not data:
return {}
emp_ids = data.get('employees', [])
bank_ids = data.get('banks', [])
salary_ids = data.get('salary', [])
date_from = data.get('date_from')
date_to = data.get('date_to')
no_details = data.get('no_details', False)
report_type = data.get('report_type', 'salary')
entry_type = data.get('entry_type', 'all')
company_id = self.env['res.company'].browse(data.get('company_id'))
employees = self.env['hr.employee'].search([('id', 'in', emp_ids)])
banks = self.env['res.bank'].search([('id', 'in', bank_ids)])
salary = self.env['hr.payroll.structure'].search([('id', 'in', salary_ids)])
# Get branch setting
branch_id = self.env['ir.config_parameter'].sudo().get_param('exp_payroll_custom.branch')
branch = True if branch_id == 'True' else False
# Get report title based on type (Arabic)
report_titles = {
'salary': 'مسير البنك للرواتب',
'allowance': 'مسير البنك للحوافز',
'overtime': 'مسير البنك للعمل الإضافي',
'training': 'مسير البنك للتدريب',
'mission': 'مسير البنك لمهام العمل',
}
report_title = report_titles.get(report_type, 'مسير البنك للرواتب')
payslip_data = []
if no_details:
# Group by bank
for bank in banks:
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
bank_payslips = []
if payslip_ids:
for payslip in payslip_ids:
payslip_dict = self._prepare_payslip_data(payslip, report_type)
bank_payslips.append(payslip_dict)
payslip_data.append({
'bank_name': bank.name,
'payslips': bank_payslips
})
else:
# No grouping - all payslips in one list
all_payslips = []
for bank in banks:
payslip_ids = self._get_payslips(date_from, date_to, entry_type, employees, salary, salary_ids, bank)
if payslip_ids:
for payslip in payslip_ids:
payslip_dict = self._prepare_payslip_data(payslip, report_type)
all_payslips.append(payslip_dict)
payslip_data = all_payslips
date_from_str = str(date_from) if date_from else ''
date_to_str = str(date_to) if date_to else ''
return {
'doc_ids': docids,
'doc_model': 'payroll.bank.wiz',
'docs': self.env['payroll.bank.wiz'].browse(docids),
'date_from': date_from_str,
'date_to': date_to_str,
'no_details': no_details,
'report_type': report_type,
'report_title': report_title,
'payslip_data': payslip_data,
'company': company_id,
}
def _prepare_payslip_data(self, payslip, report_type):
"""Prepare payslip data dictionary based on report type"""
# Get national ID safely
national_id = ''
try:
if hasattr(payslip.employee_id, 'check_nationality') and payslip.employee_id.check_nationality:
if hasattr(payslip.employee_id, 'saudi_number') and payslip.employee_id.saudi_number:
national_id = payslip.employee_id.saudi_number.saudi_id or ''
else:
if hasattr(payslip.employee_id, 'iqama_number') and payslip.employee_id.iqama_number:
national_id = payslip.employee_id.iqama_number.iqama_id or ''
except:
national_id = ''
iban = ' '
bank_code = ' '
if payslip.employee_id.bank_account_id:
iban = payslip.employee_id.bank_account_id.acc_number or ' '
if payslip.employee_id.bank_account_id.bank_id:
bank_code = payslip.employee_id.bank_account_id.bank_id.bic or ' '
if report_type == 'salary':
salary_rules = self.env['hr.salary.rule'].search([]).sorted(key=lambda v: v.sequence).ids
payslip_line_obj = self.env['hr.payslip.line']
basic = 0.0
housing = 0.0
payslip_lines_ids = payslip_line_obj.search([('slip_id', '=', payslip.id)])
for payslip_line_rec in payslip_lines_ids:
if payslip_line_rec.salary_rule_id.id in salary_rules:
if payslip_line_rec.salary_rule_id.rules_type == 'salary':
basic += payslip_line_rec.total
elif payslip_line_rec.salary_rule_id.rules_type == 'house':
housing += payslip_line_rec.total
other = round((payslip.total_allowances - basic - housing), 2)
return {
'total_amount': payslip.total_sum or 0.0,
'iban': iban,
'name': payslip.employee_id.name or ' ',
'bank_code': bank_code,
'description': '',
'basic_salary': basic,
'housing': housing,
'other': other,
'deductions': round((payslip.total_deductions + payslip.total_loans), 2),
'national_id': national_id,
}
elif report_type == 'allowance':
# For allowances, show total allowances as basic
return {
'total_amount': payslip.total_sum or 0.0,
'iban': iban,
'name': payslip.employee_id.name or ' ',
'bank_code': bank_code,
'description': '',
'basic_salary': payslip.total_allowances or 0.0,
'housing': 0.0,
'other': 0.0,
'deductions': round((payslip.total_deductions + payslip.total_loans), 2),
'national_id': national_id,
}
elif report_type in ['overtime', 'mission', 'training']:
# For overtime, mission, training - show as other earnings
return {
'total_amount': payslip.total_sum or 0.0,
'iban': iban,
'name': payslip.employee_id.name or ' ',
'bank_code': bank_code,
'description': '',
'basic_salary': 0.0,
'housing': 0.0,
'other': payslip.total_allowances or 0.0,
'deductions': round((payslip.total_deductions + payslip.total_loans), 2),
'national_id': national_id,
}
def _get_payslips(self, date_from, date_to, entry_type, employees, salary, salary_ids, bank):
"""Helper method to get payslips based on criteria"""
domain_base = [
('date_from', '>=', date_from),
('date_to', '<=', date_to),
('payslip_run_id.state', 'in', ['confirmed', 'transfered']),
('employee_id.bank_account_id.bank_id', '=', bank.id)
]
if entry_type == 'all':
domain = domain_base.copy()
if employees:
domain.append(('employee_id', 'in', employees.ids))
elif salary:
domain.append(('struct_id', 'in', salary_ids))
elif entry_type == 'posted':
domain = domain_base.copy()
if employees:
domain.extend([
('employee_id', 'in', employees.ids),
'|',
('move_id.state', '=', 'posted'),
('payslip_run_id.move_id.state', '=', 'posted')
])
elif salary:
domain.extend([
('struct_id', 'in', salary_ids),
'|',
('move_id.state', '=', 'posted'),
('payslip_run_id.move_id.state', '=', 'posted')
])
elif entry_type == 'unposted':
domain = domain_base.copy()
if employees:
domain.extend([
('employee_id', 'in', employees.ids),
('move_id.state', '!=', 'posted'),
('payslip_run_id.move_id.state', '!=', 'posted')
])
elif salary:
domain.extend([
('struct_id', 'in', salary_ids),
('move_id.state', '!=', 'posted'),
('payslip_run_id.move_id.state', '!=', 'posted')
])
else:
domain = domain_base
return self.env['hr.payslip'].search(domain)

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="report_payroll_bank_albilad_xlsx" model="ir.actions.report">
<field name="name">Al-Bilad Bank Payroll Report</field>
<field name="model">payroll.bank.wiz</field>
<field name="report_type">xlsx</field>
<field name="report_name">exp_payroll_albilad.payroll_bank_albilad_xlsx</field>
<field name="report_file">exp_payroll_albilad.payroll_bank_albilad_xlsx</field>
<field name="binding_model_id" ref="exp_payroll_custom.model_payroll_bank_wiz"/>
<field name="binding_type">report</field>
</record>
</odoo>

View File

@ -0,0 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import payroll_bank_report

View File

@ -0,0 +1,96 @@
# -*- coding:utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import UserError
class BankPayslipReportAlbilad(models.TransientModel):
_inherit = 'payroll.bank.wiz'
bank_type = fields.Selection(
selection_add=[('albilad', 'Al-Bilad Bank')],
ondelete={'albilad': 'set default'}
)
def print_report(self):
if self.bank_type == 'albilad':
# Validate that banks are selected
if not self.bank_ids:
raise UserError(_('Please select at least one bank to generate the report.'))
[data] = self.read()
date_from = self.date_from
date_to = self.date_to
no_details = self.no_details
report_type = self.report_type
entry_type = self.entry_type
bank_type = self.bank_type
employees = self.env['hr.employee'].search([('id', 'in', self.employee_ids.ids)])
banks = self.env['res.bank'].search([('id', 'in', self.bank_ids.ids)])
salary = self.env['hr.payroll.structure'].search([('id', 'in', self.salary_ids.ids)])
company_id = self.env['res.company'].search([('id', '=', self.company_id.id)])
datas = {
'employees': employees.ids,
'banks': banks.ids,
'salary': salary.ids,
'form': data,
'date_from': date_from,
'date_to': date_to,
'no_details': no_details,
'report_type': report_type,
'entry_type': entry_type,
'bank_type': bank_type,
'company_id': company_id.id,
}
return self.env.ref('exp_payroll_albilad.report_payroll_bank_albilad_xlsx').report_action(self, data=datas)
else:
return super(BankPayslipReportAlbilad, self).print_report()
def print_pdf_report(self):
if self.bank_type == 'albilad':
if not self.bank_ids:
raise UserError(_('Please select at least one bank to generate the report.'))
[data] = self.read()
date_from = self.date_from
date_to = self.date_to
no_details = self.no_details
report_type = self.report_type
entry_type = self.entry_type
bank_type = self.bank_type
employees = self.env['hr.employee'].search([('id', 'in', self.employee_ids.ids)])
banks = self.env['res.bank'].search([('id', 'in', self.bank_ids.ids)])
salary = self.env['hr.payroll.structure'].search([('id', 'in', self.salary_ids.ids)])
company_id = self.env['res.company'].search([('id', '=', self.company_id.id)])
datas = {
'employees': employees.ids,
'banks': banks.ids,
'salary': salary.ids,
'form': data,
'date_from': date_from,
'date_to': date_to,
'no_details': no_details,
'report_type': report_type,
'entry_type': entry_type,
'bank_type': bank_type,
'company_id': company_id.id,
}
return self.env.ref('exp_payroll_albilad.report_payroll_bank_albilad_pdf').report_action(self, data=datas)
else:
# Call parent method for other bank types if it exists
if hasattr(super(BankPayslipReportAlbilad, self), 'print_pdf_report'):
return super(BankPayslipReportAlbilad, self).print_pdf_report()
else:
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': _('PDF report is only available for Al-Bilad bank type.'),
'type': 'warning',
'sticky': False,
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="bank_payroll_report_form_albilad" model="ir.ui.view">
<field name="name">payroll.bank.wiz.form.albilad</field>
<field name="model">payroll.bank.wiz</field>
<field name="inherit_id" ref="exp_payroll_custom.bank_payroll_report_form"/>
<field name="arch" type="xml">
<field name="bank_ids" position="attributes">
<attribute name="required">[('bank_type', '=', 'albilad')]</attribute>
</field>
</field>
</record>
</odoo>