From 9e20d957341e5fc6df7e0791d320ad9b1c42a4b0 Mon Sep 17 00:00:00 2001 From: younes Date: Thu, 23 Oct 2025 15:38:51 +0100 Subject: [PATCH 1/2] [IMP] odex_benefit: Add Bank report for families --- odex25_ensan/odex_benefit/__manifest__.py | 8 +- odex25_ensan/odex_benefit/models/benefit.py | 9 ++ odex25_ensan/odex_benefit/reports/__init__.py | 3 +- .../reports/family_bank_report.py | 94 ++++++++++++++ .../reports/family_bank_report.xml | 12 ++ .../odex_benefit/security/ir.model.access.csv | 3 +- .../odex_benefit/views/actions_and_menus.xml | 5 + .../odex_benefit/views/benefit_view.xml | 2 + odex25_ensan/odex_benefit/wizards/__init__.py | 1 + .../wizards/family_bank_report_wizard.py | 120 ++++++++++++++++++ .../wizards/family_bank_report_wizard.xml | 42 ++++++ 11 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 odex25_ensan/odex_benefit/reports/family_bank_report.py create mode 100644 odex25_ensan/odex_benefit/reports/family_bank_report.xml create mode 100644 odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.py create mode 100644 odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.xml diff --git a/odex25_ensan/odex_benefit/__manifest__.py b/odex25_ensan/odex_benefit/__manifest__.py index 0e5bf0f39..d4bc3829d 100644 --- a/odex25_ensan/odex_benefit/__manifest__.py +++ b/odex25_ensan/odex_benefit/__manifest__.py @@ -7,8 +7,8 @@ 'website': 'http://exp-sa.com', 'license': 'GPL-3', 'author': 'Expert Ltd', - 'depends': ['base','survey', 'takaful_core', 'website', 'account', 'report_xlsx', 'sale', 'product', 'stock', 'hr', - 'purchase','web_google_maps','odex25_account_payment_fix','otp_sms_auth_custom'], + 'depends': ['base', 'survey', 'takaful_core', 'website', 'account', 'report_xlsx', 'sale', 'product', 'stock', 'hr', + 'purchase', 'web_google_maps', 'odex25_account_payment_fix', 'otp_sms_auth_custom'], 'data': [ 'security/security_view.xml', 'security/ir.model.access.csv', @@ -65,9 +65,11 @@ 'wizards/visit_location_otp_wizard_view.xml', 'views/benefit_vehicle_model.xml', 'wizards/visit_location_refused_wizard_view.xml', + 'wizards/family_bank_report_wizard.xml', 'views/visit_survey.xml', 'views/actions_and_menus.xml', -], + 'reports/family_bank_report.xml', + ], 'external_dependencies': { 'python': ['num2words'], }, diff --git a/odex25_ensan/odex_benefit/models/benefit.py b/odex25_ensan/odex_benefit/models/benefit.py index e84758ed9..c2137f97f 100644 --- a/odex25_ensan/odex_benefit/models/benefit.py +++ b/odex25_ensan/odex_benefit/models/benefit.py @@ -70,6 +70,7 @@ class GrantBenefitProfile(models.Model): family_bank = fields.Many2one('res.partner.bank') acc_number = fields.Char('Account Number',copy=False) acc_holder_name = fields.Char('Account Holder Name') + acc_holder_id_number = fields.Char(string="Account Holder ID Number") bank_id = fields.Many2one("res.bank",string='Bank') account_relation = fields.Many2one('relation.settings',string="Account Owner Relation") orphan_status = fields.Selection( @@ -1033,6 +1034,14 @@ class GrantBenefitProfile(models.Model): 'message': _('Non-Saudi mothers and fathers cannot register')} return res + @api.constrains('acc_holder_id_number') + def _check_acc_holder_id_number(self): + for record in self: + id_number = record.acc_holder_id_number + if id_number: + if not re.match(r'^\d{10}$', id_number): + raise ValidationError(_("Account Holder ID Number must contain exactly 10 digits.")) + @api.constrains('father_id_number', 'mother_id_number', 'replacement_mother_id_number') def _onchange_id_numbers(self): id_numbers = { diff --git a/odex25_ensan/odex_benefit/reports/__init__.py b/odex25_ensan/odex_benefit/reports/__init__.py index 52dc92d79..36787abff 100644 --- a/odex25_ensan/odex_benefit/reports/__init__.py +++ b/odex25_ensan/odex_benefit/reports/__init__.py @@ -1 +1,2 @@ -from . import benefit_report \ No newline at end of file +from . import benefit_report +from . import family_bank_report \ No newline at end of file diff --git a/odex25_ensan/odex_benefit/reports/family_bank_report.py b/odex25_ensan/odex_benefit/reports/family_bank_report.py new file mode 100644 index 000000000..7939d91fd --- /dev/null +++ b/odex25_ensan/odex_benefit/reports/family_bank_report.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +from odoo import api, fields, models, tools, _ +from odoo.exceptions import ValidationError, UserError +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT + + +class FamilyBankReportXlsx(models.AbstractModel): + _name = 'report.odex_benefit.family_bank_report_xlsx' + _description = 'Family Bank Report XLSX' + _inherit = 'report.report_xlsx.abstract' + + @api.model + def generate_xlsx_report(self, workbook, data, objs): + sheet = workbook.add_worksheet(_('Family Bank Report')) + if self.env.user.lang != 'en_US': + sheet.right_to_left() + + header_format = workbook.add_format({ + 'bold': True, + 'align': 'center', + 'valign': 'vcenter', + 'bg_color': '#4472C4', + 'font_color': 'white', + 'border': 1, + 'text_wrap': True + }) + + subheader_format = workbook.add_format({ + 'bold': True, + 'align': 'center', + 'valign': 'vcenter', + 'bg_color': '#D3D3D3', + 'border': 1, + 'font_size': 9 + }) + + normal = workbook.add_format({ + 'align': 'center', + 'valign': 'vcenter', + 'border': 1 + }) + + bold = workbook.add_format({ + 'bold': True, + 'align': 'center', + 'valign': 'vcenter', + 'border': 1 + }) + + sheet.set_row(0, 30) + sheet.set_row(1, 25) + sheet.set_row(2, 25) + + sheet.merge_range('A1:A2', 'Bank Name' + '\nاسم البنك', header_format) + sheet.merge_range('B1:B2', 'Account Number(24N)' + '\nرقم الحساب', header_format) + sheet.merge_range('C1:C2', 'Beneficiary Name' + '\nاسم المستفيد', header_format) + sheet.merge_range('D1:D2', 'Amount(15)' + '\nقيمة الحوالة', header_format) + sheet.merge_range('E1:E2', 'Civilian_Id(20)' + '\nرقم الهوية', header_format) + sheet.merge_range('F1:F2', 'Currency (3N)' + '\nالعملة', header_format) + sheet.merge_range('G1:G2', 'Beneficiaries Remarks (30)' + '\nملاحظات المستفيد', header_format) + sheet.merge_range('H1:H2', 'Payment Purpose' + '\nغرض الدفع', header_format) + sheet.merge_range('I1:I2', 'Beneficiary Email' + '\nالبريد الإلكتروني للمستفيد', header_format) + + row = 3 + total_amount = 0 + for line in data['lines']: + sheet.write(row, 0, line['bank_name'], normal) + sheet.write(row, 1, line['account_number'], normal) + sheet.write(row, 2, line['beneficiary_name'], normal) + sheet.write_number(row, 3, line['transfer_amount'], normal) + sheet.write(row, 4, line['beneficiary_id'], normal) + sheet.write(row, 5, line['currency'], normal) + sheet.write(row, 6, line['family_code'], normal) + sheet.write(row, 7, line['purpose'], normal) + sheet.write(row, 8, line.get('beneficiary_email', ''), normal) + total_amount += line['transfer_amount'] + row += 1 + + + sheet.merge_range(row + 1, 0, row + 1, 2, "Total" + " / " + "الإجمالي", bold) + sheet.write_number(row + 1, 3, total_amount, bold) + sheet.write(row + 1, 4, "", bold) + sheet.write(row + 1, 5, "SAR", bold) + + sheet.set_column('A:A', 25) + sheet.set_column('B:B', 25) + sheet.set_column('C:C', 30) + sheet.set_column('D:D', 15) + sheet.set_column('E:E', 20) + sheet.set_column('F:F', 12) + sheet.set_column('G:G', 25) + sheet.set_column('H:H', 20) + sheet.set_column('I:I', 30) diff --git a/odex25_ensan/odex_benefit/reports/family_bank_report.xml b/odex25_ensan/odex_benefit/reports/family_bank_report.xml new file mode 100644 index 000000000..7fbdf366a --- /dev/null +++ b/odex25_ensan/odex_benefit/reports/family_bank_report.xml @@ -0,0 +1,12 @@ + + + + + family.bank.report.wizard + Family Bank Report (Excel) + xlsx + odex_benefit.family_bank_report_xlsx + odex_benefit.family_bank_report_xlsx + + + \ No newline at end of file diff --git a/odex25_ensan/odex_benefit/security/ir.model.access.csv b/odex25_ensan/odex_benefit/security/ir.model.access.csv index 17255f920..2f7af5c5b 100644 --- a/odex25_ensan/odex_benefit/security/ir.model.access.csv +++ b/odex25_ensan/odex_benefit/security/ir.model.access.csv @@ -166,4 +166,5 @@ access_survey_user_input_group_benefit_info,survey.user_input.group_benefit_info access_survey_user_input_line_group_benefit_info,survey.user_input.line.group_benefit_info,survey.model_survey_user_input_line,odex_benefit.group_benefit_info,1,0,0,0 access_grant_benefit_account_move_line,access_grant_benefit_account_move_line,model_account_move_line,odex_benefit.group_benefit_info,1,0,0,0 access_grant_benefit_account_move,access_grant_benefit_account_move,model_account_move,odex_benefit.group_benefit_info,1,0,0,0 -access_benefit_expense_line,access_benefit_expense_line,model_benefit_expense_line,base.group_user,1,1,1,1 \ No newline at end of file +access_benefit_expense_line,access_benefit_expense_line,model_benefit_expense_line,base.group_user,1,1,1,1 +access_family_bank_report_wizard,access_family_bank_report_wizard,model_family_bank_report_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/odex25_ensan/odex_benefit/views/actions_and_menus.xml b/odex25_ensan/odex_benefit/views/actions_and_menus.xml index 6e8b5faf1..12d84a9ae 100644 --- a/odex25_ensan/odex_benefit/views/actions_and_menus.xml +++ b/odex25_ensan/odex_benefit/views/actions_and_menus.xml @@ -1039,6 +1039,11 @@ parent="education_main_menu" action="education_exam_type_action" sequence="9"/> + diff --git a/odex25_ensan/odex_benefit/views/benefit_view.xml b/odex25_ensan/odex_benefit/views/benefit_view.xml index 13516281d..844c20ca7 100644 --- a/odex25_ensan/odex_benefit/views/benefit_view.xml +++ b/odex25_ensan/odex_benefit/views/benefit_view.xml @@ -1371,6 +1371,8 @@ groups="!odex_benefit.group_benefit_manager"/> + 0 and l.benefit_family_id): + family = line.benefit_family_id + beneficiary_name = '' + beneficiary_id = '' + if family.is_family_member: + beneficiary_name = family.family_member_id.name + beneficiary_id = family.family_member_id.member_id_number + else: + beneficiary_name = family.acc_holder_name + beneficiary_id = family.acc_holder_id_number + lines_data.append({ + 'bank_name': family.bank_id.name or '', + 'account_number': 'SA' + family.acc_number or '', + 'beneficiary_name': beneficiary_name, + 'transfer_amount': line.debit or 0.0, + 'beneficiary_id': beneficiary_id, + 'currency': 'SAR', + 'family_code': family.code or '', + 'purpose': 'Others', + }) + + if not lines_data: + raise UserError(_("No debit lines with family information found.")) + datas = { + 'start_date': self.start_date, + 'end_date': self.end_date, + 'lines': lines_data, + } + return self.env.ref('odex_benefit.action_family_bank_report_xlsx').report_action(self, data=datas) diff --git a/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.xml b/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.xml new file mode 100644 index 000000000..8afb570b9 --- /dev/null +++ b/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.xml @@ -0,0 +1,42 @@ + + + + + family.bank.report.wizard.form + family.bank.report.wizard + +
+ + + + + + + + + + + + + + + +
+
+
+
+
+ + + Family Bank Report + family.bank.report.wizard + form + + new + + +
+
\ No newline at end of file From ac0883deac55dafaa351802ade780e574b984883 Mon Sep 17 00:00:00 2001 From: younes Date: Thu, 23 Oct 2025 15:52:13 +0100 Subject: [PATCH 2/2] [IMP] odex_benefit: Add Bank report for families --- odex25_ensan/odex_benefit/i18n/ar_001.po | 49 +++++++++++++++ .../wizards/family_bank_report_wizard.py | 61 ------------------- 2 files changed, 49 insertions(+), 61 deletions(-) diff --git a/odex25_ensan/odex_benefit/i18n/ar_001.po b/odex25_ensan/odex_benefit/i18n/ar_001.po index 64fcdd617..23de5f18a 100644 --- a/odex25_ensan/odex_benefit/i18n/ar_001.po +++ b/odex25_ensan/odex_benefit/i18n/ar_001.po @@ -15944,6 +15944,7 @@ msgstr "بإنتظار مساعد المدير العام" #. module: odex_benefit #: model:ir.model.fields,field_description:odex_benefit.field_benefit_expense_line__start_date #: model:ir.model.fields,field_description:odex_benefit.field_confirm_benefit_expense__start_date +#: model:ir.model.fields,field_description:odex_benefit.field_family_bank_report_wizard__start_date #: model:ir.model.fields,field_description:odex_benefit.field_service_request__new_start #: model:ir.model.fields,field_description:odex_benefit.field_service_request__start msgid "Start Date" @@ -15953,6 +15954,7 @@ msgstr "تاريخ البداية" #: model:ir.model.fields,field_description:odex_benefit.field_benefit_expense_line__end_date #: model:ir.model.fields,field_description:odex_benefit.field_confirm_benefit_expense__end_date #: model:ir.model.fields,field_description:odex_benefit.field_service_request__end +#: model:ir.model.fields,field_description:odex_benefit.field_family_bank_report_wizard__end_date #: model:ir.model.fields,field_description:odex_benefit.field_service_request__new_end msgid "End Date" msgstr "تاريخ النهاية" @@ -16556,3 +16558,50 @@ msgstr "" "بعض الأسر المحددة ليست في حالة معتمدة أو تم إيقافها:\n" "%s" +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_grant_benefit__acc_holder_id_number +msgid "Account Holder ID Number" +msgstr "رقم هوية صاحب الحساب" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/benefit.py:0 +#, python-format +msgid "Account Holder ID Number must contain exactly 10 digits." +msgstr "يجب أن يحتوي رقم هوية صاحب الحساب على 10 أرقام بالضبط." + +#. module: odex_benefit +#: model:ir.model,name:odex_benefit.model_report_odex_benefit_family_bank_report_xlsx +msgid "Family Bank Report XLSX" +msgstr "تقرير البنك للأسر XLSX" + +#. module: odex_benefit +#: code:addons/odex_benefit/reports/family_bank_report.py:0 +#: model:ir.actions.act_window,name:odex_benefit.action_family_bank_report_wizard +#: model:ir.model,name:odex_benefit.model_family_bank_report_wizard +#: model:ir.ui.menu,name:odex_benefit.menu_family_bank_report_root +#: model_terms:ir.ui.view,arch_db:odex_benefit.view_family_bank_report_wizard_form +#, python-format +msgid "Family Bank Report" +msgstr "تقرير البنك للأسر" + +#. module: odex_benefit +#: model:ir.actions.report,name:odex_benefit.action_family_bank_report_xlsx +msgid "Family Bank Report (Excel)" +msgstr "تقرير البنك للأسر (إكسل)" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_family_bank_report_wizard__move_ids +msgid "Monthly Expenses" +msgstr "المصروفات الشهرية" + +#. module: odex_benefit +#: code:addons/odex_benefit/wizards/family_bank_report_wizard.py:0 +#, python-format +msgid "Please select at least one Monthly Expense." +msgstr "يرجى اختيار مصروف شهري واحد على الأقل." + +#. module: odex_benefit +#: code:addons/odex_benefit/wizards/family_bank_report_wizard.py:0 +#, python-format +msgid "No debit lines with family information found." +msgstr "لم يتم العثور على قيود تحتوي على معلومات الأسر." \ No newline at end of file diff --git a/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.py b/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.py index 27aa38944..1b85cd441 100644 --- a/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.py +++ b/odex25_ensan/odex_benefit/wizards/family_bank_report_wizard.py @@ -4,67 +4,6 @@ from odoo import models, fields, api, _ from odoo.exceptions import UserError from odoo.tools import date_utils - -class GrantRefusedReasonWizard(models.TransientModel): - _name = 'entity.black.list.wizard' - - _description = "Entity Black List Wizard" - - def _default_entity(self): - return self._context.get('active_id') - - def _default_state(self): - return self._context.get('state') - - entity_id = fields.Many2one("grant.benefit", string="Entity", default=_default_entity) - state = fields.Selection([ - ('draft', 'Draft'), - ('first_refusal', 'First Refusal'), - ('approve', 'Approved'), - ('record_end_date', 'Record end date'), - ('refused', 'Refused'), - ('black_list', 'Black List'), - ], string="State", default=_default_state) - black_list_reason = fields.Text(string='Black List Reason', required=True) - black_list_message = fields.Text(string='Black List Message') - - # @api.multi - def create_action(self): - """Throw pop up to write the black list reason for grant""" - partner_ids = [] - for rec in self: - if rec.entity_id: - result = rec.entity_id.sudo().write({ - "state": rec.state, - "black_list_reason": rec.black_list_reason, - "black_list_message": rec.black_list_message, - }) - # grant_ids = self.env['grant.task'].search([('entity_id', '=', rec.entity_id.id)]).unlink() - subject = _('Entity') - state_label = dict(rec.fields_get(allfields=['state'])['state']['selection'])[rec.state] - body = ' '.join( - (_(u'The Entity '), rec.entity_id.name, _(u' State changed to '), state_label, u'.')).encode( - 'utf-8') - partner_ids += [(6, 0, rec.entity_id.message_follower_ids.ids)] - message_vals = { - 'subject': subject, - 'body': body, - 'partner_ids': partner_ids, - } - rec.entity_id.message_post(body=body, subject=subject, - message_type='email') - user = self.env['res.users'].search([('partner_id', '=', rec.entity_id.partner_id.id)], limit=1) - - user.sudo().write({ - 'groups_id': [(3, self.env.ref('base.group_erp_manager', False).id), - ], - - }) - rec.entity_id.send_black_list_email() - - return {'type': 'ir.actions.act_window_close'} - - class FamilyBankReportWizard(models.TransientModel): _name = 'family.bank.report.wizard' _description = 'Family Bank Report'