From 4fa290f47e4cf75e9ec1ddc6f3e53e779cf472a7 Mon Sep 17 00:00:00 2001 From: younes Date: Thu, 22 Jan 2026 14:51:34 +0100 Subject: [PATCH] [IMP] odex_benefit: IMP benefit --- odex25_benefit/odex_benefit/i18n/ar_001.po | 21 +-- .../odex_benefit/models/benefit_config.py | 44 +++++ .../odex_benefit/models/family_members.py | 10 +- .../odex_benefit/models/service_request.py | 154 ++++++++++++------ .../odex_benefit/models/services_settings.py | 8 + .../odex_benefit/security/ir.model.access.csv | 7 +- .../views/benefit_config_view.xml | 1 + .../odex_benefit/views/benefit_view.xml | 4 +- .../odex_benefit/views/family_members.xml | 9 +- .../odex_benefit/views/service_request.xml | 21 ++- .../odex_benefit/views/services_settings.xml | 1 + 11 files changed, 199 insertions(+), 81 deletions(-) diff --git a/odex25_benefit/odex_benefit/i18n/ar_001.po b/odex25_benefit/odex_benefit/i18n/ar_001.po index ce4f7f5c8..08a04cc93 100644 --- a/odex25_benefit/odex_benefit/i18n/ar_001.po +++ b/odex25_benefit/odex_benefit/i18n/ar_001.po @@ -5184,7 +5184,7 @@ msgstr "خدمات استثنائية" #: model:ir.model.fields,field_description:odex_benefit.field_family_member__is_excluded_suspension #: model:ir.model.fields,field_description:odex_benefit.field_grant_benefit__is_excluded_suspension msgid "Excluded from suspension?" -msgstr "مستثنى من إيقاف الخدمة" +msgstr "استثناء" #. module: odex_benefit #: model:ir.model.fields,field_description:odex_benefit.field_benefit_category__expenses_ids @@ -16408,12 +16408,6 @@ msgstr "لا يمكنك طلب هذه الخدمة أكثر من مرة." msgid "You cannot request more than %s" msgstr "لا يمكنك طلب أكثر من %s" -#. module: odex_benefit -#: code:addons/odex_benefit/models/service_request.py:0 -#, python-format -msgid "You cannot request this device more than %s times within %s %s." -msgstr "لا يمكنك طلب هذا الجهاز أكثر من %s مرات خلال %s %s." - #. module: odex_benefit #: code:addons/odex_benefit/models/service_request.py:0 #, python-format @@ -16880,15 +16874,14 @@ msgid "Has Marriage Course" msgstr "الحصول على دورة تأهيلية" #. module: odex_benefit -#: model_terms:ir.ui.view,arch_db:odex_benefit.service_request_form #: model_terms:ir.ui.view,arch_db:odex_benefit.seasonal_service_form_view -#, fuzzy msgid "Total Moves" -msgstr "" -"#-#-#-#-# catalog.po (Odoo Server 14.0) #-#-#-#-#\n" -"القيد المحاسبي\n" -"#-#-#-#-# catalog.po (Odoo Server 14.0) #-#-#-#-#\n" -"فاتورة العثيم" +msgstr "القيد المحاسبي" + +#. module: odex_benefit +#: model_terms:ir.ui.view,arch_db:odex_benefit.service_request_form +msgid "Total Moves" +msgstr "فاتورة" #. module: odex_benefit #: model_terms:ir.ui.view,arch_db:odex_benefit.service_request_form diff --git a/odex25_benefit/odex_benefit/models/benefit_config.py b/odex25_benefit/odex_benefit/models/benefit_config.py index b810e6d41..c2187fb6d 100644 --- a/odex25_benefit/odex_benefit/models/benefit_config.py +++ b/odex25_benefit/odex_benefit/models/benefit_config.py @@ -783,6 +783,7 @@ class BranchSettings(models.Model): 'branch.settings', string="Replacement Branch" ) + service_producer_id = fields.Many2one('res.partner', string='Service Producer') class RelationSettings(models.Model): _name = 'relation.settings' @@ -984,6 +985,49 @@ class HomeFurnishingItems(models.Model): price_second_attach = fields.Many2many('ir.attachment','rel_second_price_attachments', 'furnishing_id', 'attach_id',string="Second Price Attachment") service_request_id = fields.Many2one('service.request',string='Service Request') +class ElectricalDeviceLine(models.Model): + _name = 'electrical.device.line' + _description = 'Electrical Device Line' + + service_request_id = fields.Many2one('service.request',string='Service Request',ondelete='cascade',) + device_id = fields.Many2one('electrical.devices',string='Device',compute='_compute_device_id',store=True) + product_id = fields.Many2one('product.product',string='Product',required=True,) + quantity = fields.Integer(string='Quantity',required=True,default=1) + unit_price = fields.Float( string='Unit Price',compute='_compute_unit_price',store=True) + subtotal = fields.Float(string='Subtotal',compute='_compute_subtotal',store=True) + + @api.depends('device_id') + def _compute_unit_price(self): + for line in self: + if line.device_id: + line.unit_price = line.product_id.standard_price + else: + line.unit_price = 0.0 + + @api.depends('quantity', 'unit_price') + def _compute_subtotal(self): + for line in self: + line.subtotal = line.quantity * line.unit_price + + @api.depends('product_id', 'service_request_id.benefit_member_count', 'service_request_id.service_cat') + def _compute_device_id(self): + for line in self: + if not line.product_id or not line.service_request_id or not line.service_request_id.service_cat: + line.device_id = False + continue + + member_count = line.service_request_id.benefit_member_count or 0 + + matching = line.service_request_id.service_cat.electrical_devices_lines.filtered( + lambda d: line.product_id in d.product_ids + and (d.min_count_member or 0) <= member_count <= (d.max_count_member or 999999) + ) + + if matching: + line.device_id = matching[0] + else: + line.device_id = False + class PropertyTypeSettings(models.Model): _name = 'property.type.settings' _description = 'Property Type Settings' diff --git a/odex25_benefit/odex_benefit/models/family_members.py b/odex25_benefit/odex_benefit/models/family_members.py index f42b09ed1..45bb747a8 100644 --- a/odex25_benefit/odex_benefit/models/family_members.py +++ b/odex25_benefit/odex_benefit/models/family_members.py @@ -64,7 +64,7 @@ class FamilyMemberProfile(models.Model): # member_location = fields.Many2one('member.location', string="Member Location") birth_date = fields.Date(string="Birth Date") age = fields.Integer(string="Age", compute='_compute_get_age_date', store=True) - full_age = fields.Char(string="Full Age",compute='_compute_get_age_date',store=True) + full_age = fields.Char(string="Full Age", compute='_compute_get_age_date', store=True) age_status = fields.Selection( [('minor', 'Minor'), ('non_minor', 'Non-Minor')], string='Age Status', @@ -143,7 +143,7 @@ class FamilyMemberProfile(models.Model): last_classroom = fields.Many2one('education.classroom', string='Last Classroom') last_educational_certificate = fields.Binary(attachment=True, string='Last Educational Certificate') degree = fields.Many2one('education.result', string='Degree') - last_degree = fields.Many2one('education.result', string='Last Degree') + last_degree = fields.Many2one('education.result', string='Last Degree', compute='_compute_last_education_data', store=True) percentage = fields.Float(string="Percentage%") last_percentage = fields.Float(string="Last Percentage%") education_start_date = fields.Date(string='Education Start Date') @@ -342,9 +342,11 @@ class FamilyMemberProfile(models.Model): )[:1] rec.last_education_levels = latest_education.education_levels rec.case_study = latest_education.case_study + rec.last_degree = latest_education.degree else: rec.last_education_levels = False rec.case_study = False + rec.last_degree = False @api.depends( 'relationn.relation_type', @@ -613,7 +615,7 @@ class FamilyMemberProfile(models.Model): lambda r: r.education_status_type == 'current') if current_education_status_id: current_education_status_id = current_education_status_id.sorted('write_date', reverse=True)[:1] - if rec.state == 'second_approve' and rec.is_excluded_suspension: + if rec.is_excluded_suspension: rec.member_status = 'benefit' continue if rec.birth_date: @@ -1067,7 +1069,7 @@ class FamilyMemberProfile(models.Model): def action_exception_second_accept(self): for rec in self: - rec.is_excluded_suspension = True + # rec.is_excluded_suspension = True rec.state_a = 'exception_second_approve' # rec.is_member_workflow = False diff --git a/odex25_benefit/odex_benefit/models/service_request.py b/odex25_benefit/odex_benefit/models/service_request.py index 0bad26487..36871d8df 100644 --- a/odex25_benefit/odex_benefit/models/service_request.py +++ b/odex25_benefit/odex_benefit/models/service_request.py @@ -22,7 +22,8 @@ class ServiceRequest(models.Model): family_category = fields.Many2one('benefit.category', string='Family Category', related='family_id.benefit_category_id', store=True) family_code = fields.Char(string='Family Code', related='family_id.code', store=True) - benefit_member_count = fields.Integer(string="Benefit Member count", related='family_id.benefit_member_count',store=True) + benefit_member_count = fields.Integer(string="Benefit Member count", related='family_id.benefit_member_count', + store=True) branch_custom_id = fields.Many2one('branch.settings', string="Branch", related='family_id.branch_custom_id', store=True) member_id = fields.Many2one('family.member', string='Member') @@ -40,7 +41,7 @@ class ServiceRequest(models.Model): store=True) service_attach = fields.Many2many('ir.attachment', 'rel_service_attachment_service_request', 'service_request_id', 'attachment_id', string='Service Attachment') - requested_service_amount = fields.Float(string="Requested Service Amount", copy=False,tracking=False) + requested_service_amount = fields.Float(string="Requested Service Amount", copy=False, tracking=False) # yearly Estimated Rent Amount estimated_rent_amount = fields.Float(string="Estimated Rent Amount", compute="_get_estimated_rent_amount", store=True) @@ -113,6 +114,8 @@ class ServiceRequest(models.Model): # Fields for electrical_devices service device_id = fields.Many2one('electrical.devices', string='Device', domain="[('min_count_member','<=',benefit_member_count),('max_count_member','>=',benefit_member_count)]") + electrical_device_line_ids = fields.One2many('electrical.device.line', 'service_request_id', + string='Electrical Devices', copy=False) vendor_bill = fields.Many2one('account.move', copy=False) requested_quantity = fields.Integer(string='Requested Quantity') exception_or_steal = fields.Boolean(string='Exception Or Steal?') @@ -204,6 +207,8 @@ class ServiceRequest(models.Model): related_information_html = fields.Html(string="Related Information", compute='_compute_related_information_html', store=True, ) researcher_opinion = fields.Html(string='Specialist Opinion', tracking=True) + allowed_product_ids = fields.Many2many('product.product', string='Allowed Products', + compute='_compute_allowed_product_ids', store=True, ) def action_create_project(self): pass @@ -737,18 +742,22 @@ class ServiceRequest(models.Model): for rec in self: if rec.service_cat: rec.benefit_type = rec.service_cat.benefit_type - rec.service_producer_id = rec.service_cat.service_producer_id - # rec.payment_method = rec.service_cat.payment_method + if rec.family_id.branch_family_id.service_producer_id and rec.service_type == 'electrical_devices': + rec.service_producer_id = rec.family_id.branch_family_id.service_producer_id + else: + rec.service_producer_id = rec.service_cat.service_producer_id else: rec.benefit_type = False rec.service_producer_id = False - # rec.payment_method = 'payment_order' if not rec.family_id: rec.member_id = False rec.service_cat = False rec.available_service_cats = False + if rec.electrical_device_line_ids: + rec.electrical_device_line_ids = [(5, 0, 0)] + @api.onchange('start', 'end', 'rent_start_date', 'rent_end_date', 'payment_type') def _check_date_range(self): for rec in self: @@ -803,20 +812,15 @@ class ServiceRequest(models.Model): @api.onchange( 'requested_service_amount', - 'benefit_type', - 'date', - 'service_cat', - 'family_id', - 'member_id', - 'exception_or_steal', + 'benefit_type', 'date', + 'service_cat', 'family_id', + 'member_id', 'exception_or_steal', 'home_furnishing_exception', - 'has_marriage_course', - 'home_age', - 'device_id', - 'requested_quantity', + 'has_marriage_course', 'home_age', 'amount_for_buy_home_for_member_count', 'marriage_contract_date', - 'start', 'end' + 'start', 'end', + 'electrical_device_line_ids' ) def onchange_requested_service_amount(self): res = {} @@ -965,31 +969,48 @@ class ServiceRequest(models.Model): else: if service_type in special_services: if service_type == 'electrical_devices': - # rec.service_max_amount = rec.device_id.price_unit if rec.device_id else 0.0 - if rec.device_id: - rec.service_max_amount = rec.device_id.price_unit * rec.requested_quantity - rec.requested_service_amount = rec.service_max_amount - else: + if not rec.electrical_device_line_ids: rec.service_max_amount = 0.0 rec.requested_service_amount = 0.0 - if rec.device_id: - delta_kwargs = {period: interval} - date_before = rec.date - relativedelta(**delta_kwargs) + continue + + total_amount = sum(rec.electrical_device_line_ids.mapped('subtotal')) + rec.requested_service_amount = total_amount + rec.service_max_amount = total_amount + + delta_kwargs = {period: interval} + date_before = rec.date - relativedelta(**delta_kwargs) + device_qty_map = {} + for line in rec.electrical_device_line_ids: + if line.device_id: + device_id = line.device_id.id + device_qty_map[device_id] = device_qty_map.get(device_id, 0) + line.quantity + + for device_id, current_total_qty in device_qty_map.items(): + device = self.env['electrical.devices'].browse(device_id) + allowed_qty = device.allowed_quantity + if current_total_qty > allowed_qty: + raise ValidationError(_( + "Device '%s': Total quantity in this request (%s) exceeds the allowed limit of %s units." + ) % (device.device_name, current_total_qty, allowed_qty)) domain = base_domain + [ - ('device_id', '=', rec.device_id.id), - ('date', '>', date_before) + ('date', '>', date_before), + ('electrical_device_line_ids.device_id', '=', device_id) ] existing_requests = Service.search(domain) - total_previous_qty = sum(existing_requests.mapped('requested_quantity')) - total_qty = total_previous_qty + rec.requested_quantity - allowed_line = rec.service_cat.electrical_devices_lines.filtered( - lambda l: l.id == rec.device_id.id + existing_device_lines = existing_requests.mapped( + 'electrical_device_line_ids').filtered( + lambda l: l.device_id.id == device_id ) - allowed_qty = allowed_line.allowed_quantity if allowed_line else 0 + total_previous_qty = sum(existing_device_lines.mapped('quantity')) + total_qty = total_previous_qty + current_total_qty + if total_qty > allowed_qty: raise ValidationError(_( - "You cannot request this device more than %s times within %s %s." - ) % (allowed_qty, interval, period)) + "Device '%s': You cannot request more than %s units within %s %s. " + "(Previous requests: %s, Current request: %s, Total: %s)" + ) % (device.device_name, allowed_qty, interval, + period, total_previous_qty, current_total_qty, total_qty)) else: last_request = Service.search(base_domain, order='date desc', limit=1) if last_request and last_request.date: @@ -1173,6 +1194,19 @@ class ServiceRequest(models.Model): rec.available_service_cats = rec.available_service_cats.sudo().search(domain) + @api.depends('service_cat', 'benefit_member_count') + def _compute_allowed_product_ids(self): + for rec in self: + if not rec.service_cat or not rec.benefit_member_count or rec.service_cat.service_type != 'electrical_devices': + rec.allowed_product_ids = False + continue + + matching_devices = rec.service_cat.electrical_devices_lines.filtered( + lambda dev: (dev.min_count_member or 0) <= rec.benefit_member_count <= (dev.max_count_member or 9999) + ) + + rec.allowed_product_ids = matching_devices.mapped('product_ids') + def action_set_to_draft(self): for rec in self: rec.state = 'draft' @@ -1240,24 +1274,46 @@ class ServiceRequest(models.Model): "• Not be linked to any invoice" ) % names) + empty_requests = self.filtered(lambda r: not r.electrical_device_line_ids) + if empty_requests: + names = ", ".join(empty_requests.mapped('name')) + raise UserError(_( + "Cannot create invoice: the following requests have no electrical device lines:\n%s\n" + "All selected requests must contain at least one device line." + ) % names) + for rec in self: - invoice_line = (0, 0, { - 'name': f'{rec.family_id.name}/{rec.device_id.device_name}/{rec.description}/{rec.name}', - 'account_id': rec.device_account_id.id, - 'analytic_account_id': rec.family_id.branch_family_id.branch.analytic_account_id.id, - 'quantity': rec.requested_quantity, - 'price_unit': rec.requested_service_amount, - 'benefit_family_id': rec.family_id.id, + line_ids = [] + for line in rec.electrical_device_line_ids: + invoice_line_vals = { + 'name': rec.name or _('Electrical Device Line'), + 'product_id': line.product_id.id or False, + 'account_id': line.device_id.account_id.id or False, + 'analytic_account_id': ( + rec.family_id.branch_family_id.branch.analytic_account_id.id + if rec.family_id.branch_family_id + and rec.family_id.branch_family_id.branch + and rec.family_id.branch_family_id.branch.analytic_account_id + else False + ), + 'quantity': line.quantity, + 'price_unit': line.unit_price, + 'benefit_family_id': rec.family_id.id, + } + line_ids.append((0, 0, invoice_line_vals)) + + if not line_ids: + raise UserError(_("No invoice lines could be created.")) + vendor_bill = self.env['account.move'].create({ + 'move_type': 'in_invoice', + 'partner_id': rec.service_producer_id.id, + 'partner_shipping_id': rec.family_id.partner_id.id, + 'journal_id': validation_setting.journal_id.id, + # 'accountant_id': self.accountant_id.id, + 'invoice_line_ids': line_ids, + 'ref': rec.name or False, }) - line_ids.append(invoice_line) - vendor_bill = self.env['account.move'].create({ - 'move_type': 'in_invoice', - 'partner_id': self[0].service_producer_id.id, - 'journal_id': validation_setting.journal_id.id, - # 'accountant_id': self.accountant_id.id, - 'invoice_line_ids': line_ids, - }) - self.vendor_bill = vendor_bill + rec.vendor_bill = vendor_bill def _get_total_move_lines(self): for rec in self: diff --git a/odex25_benefit/odex_benefit/models/services_settings.py b/odex25_benefit/odex_benefit/models/services_settings.py index 42ccee3cf..0c381c6d2 100644 --- a/odex25_benefit/odex_benefit/models/services_settings.py +++ b/odex25_benefit/odex_benefit/models/services_settings.py @@ -211,6 +211,14 @@ class ElectricalDevices(models.Model): account_id = fields.Many2one('account.account', string='Expenses Account', domain="[('user_type_id.id','=',15)]") services_settings_id = fields.Many2one('services.settings') price_unit = fields.Float() + product_ids = fields.Many2many( + comodel_name='product.product', + relation='electrical_device_product_rel', + column1='electrical_device_id', + column2='product_id', + string='Allowed Products', + domain = "[('type', '=', 'product')]" + ) class HomeFurnishingLines(models.Model): diff --git a/odex25_benefit/odex_benefit/security/ir.model.access.csv b/odex25_benefit/odex_benefit/security/ir.model.access.csv index a6c89971f..11cf3b686 100644 --- a/odex25_benefit/odex_benefit/security/ir.model.access.csv +++ b/odex25_benefit/odex_benefit/security/ir.model.access.csv @@ -1,7 +1,7 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_benefit_researcher_record,access_benefit_researcher_record,model_grant_benefit,odex_benefit.group_benefit_researcher,1,1,1,0 -access_benefit_woman_commitee_record,access_benefit_record,model_grant_benefit,odex_benefit.group_benefit_woman_commitee,1,1,1,1 -access_benefit_branch_manager_record,access_benefit_branch_manager_record,model_grant_benefit,odex_benefit.group_benefit_branch_manager,1,1,1,1 +access_benefit_researcher_record,access_benefit_researcher_record,model_grant_benefit,odex_benefit.group_benefit_researcher,1,1,0,0 +access_benefit_woman_commitee_record,access_benefit_record,model_grant_benefit,odex_benefit.group_benefit_woman_commitee,1,1,0,1 +access_benefit_branch_manager_record,access_benefit_branch_manager_record,model_grant_benefit,odex_benefit.group_benefit_branch_manager,1,1,0,1 access_benefit_manager_record,access_benefit_manager_record,model_grant_benefit,odex_benefit.group_benefit_manager,1,1,1,1 access_benefit_family_services_manager,access_benefit_family_services_manager,model_grant_benefit,odex_benefit.group_family_services_manager,1,1,1,1 access_education_status_all,education_status,model_education_status,odex_benefit.group_benefit_info,1,1,1,1 @@ -201,4 +201,5 @@ access_return_reason_wizard,access_return_reason_wizard,model_return_reason_wiza access_property_type_settings,access_property_type_settings,model_property_type_settings,base.group_user,1,0,0,0 access_property_type_benefit_settings,access_property_type_benefit_settings,model_property_type_settings,odex_benefit.group_benefit_settings,1,1,1,1 access_rent_contract,access_rent_contract,model_rent_contract,base.group_user,1,1,1,1 +access_electrical_device_line,access_electrical_device_line,model_electrical_device_line,base.group_user,1,1,1,1 access_seasonal_service_payment_accountant_accept,access_seasonal_service_payment_accountant_accept,model_seasonal_service,odex_benefit.group_benefit_payment_accountant_accept,1,0,0,0 \ No newline at end of file diff --git a/odex25_benefit/odex_benefit/views/benefit_config_view.xml b/odex25_benefit/odex_benefit/views/benefit_config_view.xml index 6f804938a..00b803db5 100644 --- a/odex25_benefit/odex_benefit/views/benefit_config_view.xml +++ b/odex25_benefit/odex_benefit/views/benefit_config_view.xml @@ -956,6 +956,7 @@ + diff --git a/odex25_benefit/odex_benefit/views/benefit_view.xml b/odex25_benefit/odex_benefit/views/benefit_view.xml index 93cd70f4a..5dbbd358e 100644 --- a/odex25_benefit/odex_benefit/views/benefit_view.xml +++ b/odex25_benefit/odex_benefit/views/benefit_view.xml @@ -362,7 +362,7 @@ grant.benefit.form grant.benefit -
+
@@ -234,6 +234,7 @@ + - - + + + + + + + + + + + + + diff --git a/odex25_benefit/odex_benefit/views/services_settings.xml b/odex25_benefit/odex_benefit/views/services_settings.xml index 745bc7fa6..71882e9c1 100644 --- a/odex25_benefit/odex_benefit/views/services_settings.xml +++ b/odex25_benefit/odex_benefit/views/services_settings.xml @@ -171,6 +171,7 @@ +