diff --git a/odex25_benefit/odex_benefit/i18n/ar_001.po b/odex25_benefit/odex_benefit/i18n/ar_001.po index 330cfca57..34f5ebd3e 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 @@ -9562,6 +9562,7 @@ msgstr "هل الأسرة منتجة؟" #: model:ir.model.fields,field_description:odex_benefit.field_receive_appliances_furniture__prod_id #: model:ir.model.fields,field_description:odex_benefit.field_receive_benefit_zkat__product_id #: model:ir.model.fields,field_description:odex_benefit.field_receive_food_basket__product_id +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_device_line__product_id msgid "Product" msgstr "المنتج" @@ -9691,6 +9692,7 @@ msgstr "" #: model:ir.model.fields,field_description:odex_benefit.field_service_request__service_qty #: model:ir.model.fields,field_description:odex_benefit.field_zkat_line__quantity #: model_terms:ir.ui.view,arch_db:odex_benefit.benefit_food_surplus_form +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_device_line__quantity msgid "Quantity" msgstr "الكمية" @@ -10691,8 +10693,10 @@ msgid "Seo name" msgstr "اسم محسنات محرك البحث " #. module: odex_benefit +#: code:addons/odex_benefit/models/service_request.py:0 #: model:ir.model.fields,field_description:odex_benefit.field_service_attachments_settings__service_id #: model:ir.model.fields.selection,name:odex_benefit.selection__main_service__service_type__service +#, python-format msgid "Service" msgstr "خدمة" @@ -10734,12 +10738,14 @@ msgid "Service Name" msgstr "اسم الخدمة" #. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_branch_settings__service_producer_id #: model:ir.model.fields,field_description:odex_benefit.field_service_request__service_producer_id #: model:ir.model.fields,field_description:odex_benefit.field_services_settings__service_producer_id msgid "Service Producer" msgstr "جهة الدفع" #. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_device_line__service_request_id #: model:ir.model.fields,field_description:odex_benefit.field_home_furnishing_items__service_request_id #: model:ir.model.fields,field_description:odex_benefit.field_service_attachments_settings__service_request_id #: model_terms:ir.ui.view,arch_db:odex_benefit.service_request_form @@ -11469,12 +11475,6 @@ msgstr "" "رقم التعريف الضريبي. قم بإكماله إذا كانت جهة الاتصال تخضع للضرائب الحكومية. " "يُستخدم في بعض المستندات القانونية." -#. module: odex_benefit -#: code:addons/odex_benefit/models/benefit.py:0 -#, python-format -msgid "The account number already exists!" -msgstr "" - #. module: odex_benefit #: code:addons/odex_benefit/models/death_reason_settings.py:0 #: model:ir.model.constraint,message:odex_benefit.constraint_death_reason_settings_uniq_name @@ -16350,6 +16350,7 @@ msgid "Electrical Devices Service" msgstr "الأجهزة الكهربائية" #. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_device_line__device_id #: model:ir.model.fields,field_description:odex_benefit.field_service_request__device_id msgid "Device" msgstr "الجهاز" @@ -16414,12 +16415,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 @@ -16886,15 +16881,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 @@ -18094,11 +18088,15 @@ msgid "Resume Approval by Family Services Manager" msgstr "اعتماد مديرة خدمات المستفيدين للإعادة" #. module: odex_benefit -#: model_terms:ir.ui.view,arch_db:odex_benefit.family_member_form #: model_terms:ir.ui.view,arch_db:odex_benefit.grant_benefit_form msgid "Resume Family Service" msgstr "إعادة الأسرة للخدمة" +#. module: odex_benefit +#: model_terms:ir.ui.view,arch_db:odex_benefit.family_member_form +msgid "Resume Member Service" +msgstr "إعادة الفرد للخدمة" + #. module: odex_benefit #: model_terms:ir.ui.view,arch_db:odex_benefit.family_member_form #: model_terms:ir.ui.view,arch_db:odex_benefit.grant_benefit_form @@ -18370,14 +18368,6 @@ msgid "" "specialization." msgstr "أكبر من %s سنة وغير منتظم بتخصص علمي أو مهني." -#. module: odex_benefit -#: code:addons/odex_benefit/models/family_members.py:0 -#, python-format -msgid "" -"The member is over %s years old and not enrolled in a scientific or " -"vocational specialization." -msgstr "أكبر من %s سنة وغير منتظم بتخصص علمي أو مهني." - #. module: odex_benefit #: code:addons/odex_benefit/models/benefit.py:0 #, python-format @@ -18501,14 +18491,169 @@ msgid "Governmental/Paid" msgstr "حكومي /مقابل مادي" #. module: odex_benefit -#: code:addons/odex_benefit/models/family_members.py:0 -#: code:addons/odex_benefit/models/family_members.py:0 +#: model:ir.model.fields,field_description:odex_benefit.field_location_settings__replacement_mother_is_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_marital_status__replacement_mother_is_benefit +msgid "Replacement Mother Is Benefit?" +msgstr "هل الأم البديلة مستفيدة؟" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/benefit.py:0 #, python-format -msgid "She is over %s years of age and has no underage brothers." -msgstr "تجاوزت عمر %s وليس لديها اخوة قصر" +msgid "Family is suspended or refused." +msgstr "توقفت الأسرة أو تم رفضها." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/benefit.py:0 +#, python-format +msgid "Replacement mother marital or location information is missing." +msgstr "معلومات الحالة الاجتماعية أو موقع السكن للأم البديلة مفقودة." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/benefit.py:0 +#, python-format +msgid "The replacement mother's marital status is not eligible for benefits." +msgstr "الحالة الاجتماعية للأم البديلة غير مؤهلة للاستفادة." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/benefit.py:0 +#, python-format +msgid "The replacement mother's location is not eligible for benefits." +msgstr "مكان سكن الأم البديلة غير مؤهل للاستفادة." + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_family_member__can_request_temporary_exception +msgid "Can Request Temporary Exception" +msgstr "يمكن طلب استثناء مؤقت" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_services_settings__allowed_country_ids +msgid "Allowed Countries" +msgstr "الجنسيات المسموح بها" + +#. module: odex_benefit +#: model:ir.model.fields,help:odex_benefit.field_services_settings__allowed_country_ids +msgid "" +"Countries where this service is available. Leave empty to allow all " +"countries." +msgstr "الجنسيات التي تتوفر فيها هذه الخدمة. اتركها فارغة للسماح بجميع الجنسيات." + +#. module: odex_benefit +#: model:res.groups,name:odex_benefit.group_family_member_exception +msgid "Family Member Exception" +msgstr "استثناء أفراد الأسرة" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/benefit.py:0 +#, python-format +msgid "Non-Saudi mothers and fathers cannot register." +msgstr "الأمهات والآباء غير السعوديين." + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_family_member__full_age +msgid "Full Age" +msgstr "العمر" #. module: odex_benefit #: code:addons/odex_benefit/models/family_members.py:0 #, python-format -msgid "She is over %s years of age." -msgstr "تجاوزت عمر %s" \ No newline at end of file +msgid "%s years" +msgstr "%s سنة" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "0 years, 0 months, 0 days" +msgstr "0 سنة، 0 شهر، 0 يوم" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "%s months" +msgstr "%s شهر" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "%s day(s)" +msgstr "%s يوم" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "%s days" +msgstr "%s يوم" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "%s month(s)" +msgstr "%s شهر" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "%s year(s)" +msgstr "%s سنة" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/family_members.py:0 +#, python-format +msgid "0 days" +msgstr "0 يوم" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/service_request.py:0 +#: model:ir.model,name:odex_benefit.model_electrical_device_line +#, python-format +msgid "Electrical Device Line" +msgstr "جهاز كهربائي" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_device_line__unit_price +msgid "Unit Price" +msgstr "سعر الوحدة" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_device_line__subtotal +msgid "Subtotal" +msgstr "المجموع" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_service_request__electrical_device_line_ids +#: model:ir.model.fields.selection,name:odex_benefit.selection__services_settings__service_type__electrical_devices +msgid "Electrical Devices" +msgstr "أجهزة كهربائية" + +#. module: odex_benefit +#: model:ir.model.fields,field_description:odex_benefit.field_electrical_devices__product_ids +#: model:ir.model.fields,field_description:odex_benefit.field_service_request__allowed_product_ids +msgid "Allowed Products" +msgstr "المنتجات المسموح بها" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/service_request.py:0 +#, python-format +msgid "" +"Device '%s': Total quantity in this request (%s) exceeds the allowed limit " +"of %s units." +msgstr "الجهاز '%s': إجمالي الكمية في هذا الطلب (%s) يتجاوز الحد المسموح به وهو %s وحدة." + +#. module: odex_benefit +#: code:addons/odex_benefit/models/service_request.py:0 +#, python-format +msgid "" +"Device '%s': You cannot request more than %s units within %s %s. (Previous " +"requests: %s, Current request: %s, Total: %s)" +msgstr "الجهاز '%s': لا يمكنك طلب أكثر من %s وحدة خلال %s %s. (الطلبات السابقة: %s، الطلب الحالي: %s، المجموع: %s)" + +#. module: odex_benefit +#: model:res.groups,name:odex_benefit.group_family_services_department_manager +msgid "Family Services Department Manager" +msgstr "مدير قسم الخدمات الاسرية" + +#. module: odex_benefit +#: code:addons/odex_benefit/models/service_request.py:0 +#, python-format +msgid "" +"Cannot create invoice for request '%s': No invoice lines could be created." +msgstr "لا يمكن إنشاء فاتورة للطلب '%s': لم يتم إنشاء أي بنود في الفاتورة." \ No newline at end of file diff --git a/odex25_benefit/odex_benefit/models/account_move_line.py b/odex25_benefit/odex_benefit/models/account_move_line.py index 3d6114822..3af199d46 100644 --- a/odex25_benefit/odex_benefit/models/account_move_line.py +++ b/odex25_benefit/odex_benefit/models/account_move_line.py @@ -49,4 +49,15 @@ class AccountMove(models.Model): 'domain': [('id', 'in', attachment_ids)], 'context': ctx, 'target': 'current', - } \ No newline at end of file + } + + def _get_name_invoice_report(self): + self.ensure_one() + has_benefit_family = any( + line.benefit_family_id or line.family_confirm_id + for line in self.invoice_line_ids + ) + + if has_benefit_family or self.benefit_family_ids or self.family_confirm_id: + return 'account.report_invoice_document' + return super()._get_name_invoice_report() \ No newline at end of file diff --git a/odex25_benefit/odex_benefit/models/benefit.py b/odex25_benefit/odex_benefit/models/benefit.py index 4a557c9a3..b2fbd4303 100644 --- a/odex25_benefit/odex_benefit/models/benefit.py +++ b/odex25_benefit/odex_benefit/models/benefit.py @@ -750,10 +750,6 @@ class GrantBenefitProfile(models.Model): @api.depends('is_mother_work','mother_salary_ids','mother_expenses_ids','mother_debits_ids') def _compute_mother_net_income(self): for rec in self: - if not rec.is_mother_work: - rec.mother_net_income = 0.0 - continue - deductions = ( sum(rec.mother_expenses_ids.filtered('deduct_from_family_income').mapped('amount')) + sum(rec.mother_debits_ids.filtered('deduct_from_family_income').mapped('monthly_installment')) @@ -1409,8 +1405,10 @@ class GrantBenefitProfile(models.Model): reasons.append(_("The mother's marital or location conditions are not eligible.")) status = 'non_benefit' elif rec.mother_marital_conf.is_benefit : - if rec.is_mother_work and rec.mother_country_id.code == 'SA' or ( - rec.mother_country_id.code != 'SA' and rec.father_country_id.code == 'SA'): + if rec.mother_country_id.code != 'SA' and rec.father_country_id.code != 'SA' and not rec.mother_country_id.is_excluded and not rec.father_country_id.is_excluded: + reasons.append(_("Non-Saudi mothers and fathers cannot register.")) + status = 'non_benefit' + else: if mini_income_for_mother < rec.mother_net_income <= max_income_for_mother: reasons.append(_("The mother's income is between minimum and maximum thresholds.")) status = 'non_benefit' @@ -1418,36 +1416,31 @@ class GrantBenefitProfile(models.Model): status = 'benefit' elif rec.mother_net_income > max_income_for_mother: status = 'benefit' - elif not rec.is_mother_work and rec.mother_country_id.code == 'SA' or ( - rec.mother_country_id.code != 'SA' and rec.father_country_id.code == 'SA'): - status = 'benefit' return status, reasons def check_replacement_mother_status(self): - validation_setting = self.env["family.validation.setting"].search([], limit=1) - mini_income_for_mother = validation_setting.mini_income_for_mother - max_income_for_mother = validation_setting.max_income_for_mother for rec in self: reasons = [] status = 'benefit' if not rec.add_replacement_mother: continue - if not rec.replacement_mother_location_conf.is_benefit or not rec.replacement_mother_marital_conf.is_benefit or rec.state in ['suspended_second_approve','refused']: + if rec.state in ['suspended_second_approve', 'refused']: status = 'non_benefit' - reasons.append(_("The replacement mother's marital or location conditions are not eligible.")) - elif rec.replacement_mother_marital_conf.is_benefit: - if rec.replacement_is_mother_work and rec.replacement_mother_country_id.code == 'SA' or ( - rec.replacement_mother_country_id.code != 'SA' and rec.father_country_id.code == 'SA'): - if mini_income_for_mother < rec.replacement_mother_income <= max_income_for_mother: - status = 'non_benefit' - reasons.append(_("The replacement mother's income is between minimum and maximum thresholds.")) - elif rec.replacement_mother_income <= mini_income_for_mother: - status = 'benefit' - elif rec.replacement_mother_income > max_income_for_mother: - status = 'benefit' - elif not rec.replacement_is_mother_work and rec.replacement_mother_country_id.code == 'SA' or ( - rec.replacement_mother_country_id.code != 'SA' and rec.father_country_id.code == 'SA'): - status = 'benefit' + reasons.append(_("Family is suspended or refused.")) + return status, reasons + + if not rec.replacement_mother_marital_conf or not rec.replacement_mother_location_conf: + status = 'non_benefit' + reasons.append(_("Replacement mother marital or location information is missing.")) + return status, reasons + + if not rec.replacement_mother_marital_conf.replacement_mother_is_benefit: + status = 'non_benefit' + reasons.append(_("The replacement mother's marital status is not eligible for benefits.")) + + if not rec.replacement_mother_location_conf.replacement_mother_is_benefit: + status = 'non_benefit' + reasons.append(_("The replacement mother's location is not eligible for benefits.")) return status, reasons def delete_from_db(self): @@ -1664,18 +1657,19 @@ class GrantBenefitProfile(models.Model): if messages: raise UserError("\n".join(messages)) - if not rec.user_id: - self.sudo().create_user() + #if not rec.user_id: + # self.sudo().create_user() - rec.user_id.sudo().write({ - 'groups_id': [(3, self.env.ref('base.group_user', False).id)], - }) - # rec.user_id.sudo().write({ - # 'groups_id': [(4, self.env.ref('odex_benefit.group_benefit_user', False).id)], - # }) - rec.user_id.sudo().write({ - 'groups_id': [(4, self.env.ref('base.group_portal', False).id)], - }) + if rec.user_id: + rec.user_id.sudo().write({ + 'groups_id': [(3, self.env.ref('base.group_user', False).id)], + }) + # rec.user_id.sudo().write({ + # 'groups_id': [(4, self.env.ref('odex_benefit.group_benefit_user', False).id)], + # }) + rec.user_id.sudo().write({ + 'groups_id': [(4, self.env.ref('base.group_portal', False).id)], + }) if rec.action_type == 'new': rec.approve_date = datetime.now() self.sudo().send_approval_benefit_email() @@ -1795,14 +1789,14 @@ class GrantBenefitProfile(models.Model): # @api.multi def action_edit_info(self): - user = self.user_id - if not user: - user = self.env['res.users'].sudo().search( - [('partner_id', '=', self.partner_id.id), ('active', '=', False)]) - if user: - user.write({'active': True}) - else: - user = self.create_user() + #user = self.user_id + #if not user: + # user = self.env['res.users'].sudo().search( + # [('partner_id', '=', self.partner_id.id), ('active', '=', False)]) + # if user: + # user.write({'active': True}) + # else: + # user = self.create_user() #group_e = self.env.ref('odex_benefit.group_benefit_edit', False) try: #group_e.sudo().write({'users': [(4, user.id)]}) @@ -2195,7 +2189,7 @@ class GrantBenefitProfile(models.Model): validation_setting = self.env["family.validation.setting"].search([], limit=1) max_income_for_mother = validation_setting.max_income_for_mother for rec in self: - add_mother_net_income = (rec.mother_status == 'benefit' and rec.mother_net_income > max_income_for_mother) + add_mother_net_income = (rec.mother_location_conf.is_benefit and rec.mother_marital_conf.is_benefit and rec.mother_net_income > max_income_for_mother) if add_mother_net_income: rec.mother_income = rec.mother_net_income else: @@ -2211,18 +2205,11 @@ class GrantBenefitProfile(models.Model): for rec in self: total = 0.0 rec.total_income = 0.0 - if not rec.add_replacement_mother: - if rec.mother_status == 'non_benefit': - total = sum(rec.salary_ids.filtered(lambda e: e.approved).mapped('salary_amount')) - elif rec.mother_status == 'benefit': - total = sum(rec.salary_ids.filtered(lambda e: e.approved).mapped('salary_amount')) + rec.mother_income - rec.total_income = total - if rec.add_replacement_mother: - if rec.replacement_mother_status == 'non_benefit': - total = sum(rec.salary_ids.filtered(lambda e: e.approved).mapped('salary_amount')) - elif rec.replacement_mother_status == 'benefit': - total = sum(rec.salary_ids.filtered(lambda e: e.approved).mapped('salary_amount')) + rec.replacement_mother_income - rec.total_income += total + if rec.mother_status == 'non_benefit': + total = sum(rec.salary_ids.filtered(lambda e: e.approved).mapped('salary_amount')) + elif rec.mother_status == 'benefit': + total = sum(rec.salary_ids.filtered(lambda e: e.approved).mapped('salary_amount')) + rec.mother_income + rec.total_income = total def get_mother_name(self): for rec in self: @@ -2334,7 +2321,7 @@ class GrantBenefitProfile(models.Model): income = family_income ben.member_income = round(income, 0) - @api.depends("member_income") + @api.depends("member_income", "benefit_member_count") def get_benefit_category(self): default_category = self.env['benefit.category'].sudo().search( [('is_benefit', '=', False)], limit=1 @@ -2640,11 +2627,11 @@ class GrantBenefitProfile(models.Model): raise ValidationError(_("The IBAN number must contain exactly 22 digits.")) # Check if the account number already exists in the partner bank or benefit - partner_exist = self.env['res.partner.bank'].search([('acc_number', '=', self.acc_number)], limit=1) - benefit_exist = self.search([('acc_number', '=', self.acc_number)], limit=1) + #partner_exist = self.env['res.partner.bank'].search([('acc_number', '=', self.acc_number)], limit=1) + #benefit_exist = self.search([('acc_number', '=', self.acc_number)], limit=1) - if partner_exist or benefit_exist: - raise ValidationError(_("The account number already exists!")) + #if partner_exist or benefit_exist: + # raise ValidationError(_("The account number already exists!")) @api.onchange('mother_marital_conf', 'mother_location_conf', 'mother_net_income') def _onchange_mother_info(self): @@ -2654,7 +2641,7 @@ class GrantBenefitProfile(models.Model): 'message': _('Not Benefit')} return res - @api.onchange('replacement_mother_marital_conf', 'replacement_mother_location_conf', 'replacement_mother_income') + @api.onchange('replacement_mother_marital_conf', 'replacement_mother_location_conf') def _onchange_replacement_mother_info(self): res = {} for rec in self: @@ -2711,9 +2698,9 @@ class GrantBenefitProfile(models.Model): def update_data_automatically(self): obj = self.env["grant.benefit"].search([]) for member in obj.member_ids: - # member._compute_get_age_date() + member._compute_get_age_date() member.check_member_status() - self.action_auto_suspend() + #self.action_auto_suspend() class BenefitFollowers(models.Model): diff --git a/odex25_benefit/odex_benefit/models/benefit_config.py b/odex25_benefit/odex_benefit/models/benefit_config.py index acf02914e..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' @@ -806,6 +807,7 @@ class LocationSettings(models.Model): location_type = fields.Selection([('member', _('Member')), ('mother_location', _('Mother Location'))]) is_benefit = fields.Boolean(string='Is Benefit?') is_far_from_family = fields.Boolean(string='Is Far From Family?') + replacement_mother_is_benefit = fields.Boolean(string="Replacement Mother Is Benefit?", default=True, ) class AttachmentsSettings(models.Model): _name = 'attachments.settings' @@ -897,6 +899,7 @@ class MaritalStatus(models.Model): name = fields.Char(string="Name") is_benefit = fields.Boolean(string='Is Benefit?') is_dead = fields.Boolean(string='Is Dead?') + replacement_mother_is_benefit = fields.Boolean(string="Replacement Mother Is Benefit?",default=True,) class AgeCategory(models.Model): _name = 'age.category' @@ -982,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_expense.py b/odex25_benefit/odex_benefit/models/family_expense.py index e2431ade1..ed094b80d 100644 --- a/odex25_benefit/odex_benefit/models/family_expense.py +++ b/odex25_benefit/odex_benefit/models/family_expense.py @@ -273,13 +273,17 @@ class ConfirmBenefitExpense(models.Model): # Define base domain for family selection base_domain = ['|', ('state', '=', 'second_approve'), '&', - ('state', 'in', ('waiting_approve', 'first_approve')), ('action_type', '=', 'suspended')] + ('state', 'not in', ('temporary_suspended', 'suspended_second_approve')), ('action_type', '=', 'suspended')] if rec.branch_custom_ids: base_domain.append(('branch_custom_id', 'in', rec.branch_custom_ids.ids)) min_income = validation_setting.benefit_category_ids.mapped('mini_income_amount') max_income = validation_setting.benefit_category_ids.mapped('max_income_amount') + benefit_category_ids = validation_setting.benefit_category_ids base_domain.extend([('member_income', '>=', min(min_income)), ('member_income', '<=', max(max_income))]) - base_domain.extend([('benefit_category_id', '!=', False)]) + if benefit_category_ids: + base_domain.extend([('benefit_category_id', 'in', benefit_category_ids.ids)]) + else: + base_domain.extend([('benefit_category_id', '!=', False)]) if rec.start_date and rec.end_date: conflicting_records = self.search([ ('id', '!=', rec._origin.id), diff --git a/odex25_benefit/odex_benefit/models/family_members.py b/odex25_benefit/odex_benefit/models/family_members.py index a34409bd2..45bb747a8 100644 --- a/odex25_benefit/odex_benefit/models/family_members.py +++ b/odex25_benefit/odex_benefit/models/family_members.py @@ -64,6 +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) age_status = fields.Selection( [('minor', 'Minor'), ('non_minor', 'Non-Minor')], string='Age Status', @@ -142,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') @@ -293,6 +294,8 @@ class FamilyMemberProfile(models.Model): resume_date = fields.Date(string="Return Date") resume_notes = fields.Text(string="Return Notes") exit_benefit_date = fields.Date(string="Exit Benefit Date") + can_request_temporary_exception = fields.Boolean(string="Can Request Temporary Exception", + compute='_compute_can_request_temporary_exception', ) # def create(self, vals): # for line_vals in vals: @@ -339,9 +342,36 @@ 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', + 'case_study', + 'age', + 'benefit_id', + ) + def _compute_can_request_temporary_exception(self): + settings = self.env["family.validation.setting"].search([], limit=1) + + exceptional_age_scientific = ( + settings.exceptional_age_scientific_specialty + if settings else 22 + ) + + for rec in self: + if not rec.relationn: + rec.can_request_temporary_exception = False + continue + + is_son = rec.relationn.relation_type == 'son' + is_continuous = rec.case_study == 'continuous' + old_enough = (rec.age or 0) >= exceptional_age_scientific + + rec.can_request_temporary_exception = is_son and is_continuous and old_enough @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): @@ -422,18 +452,24 @@ class FamilyMemberProfile(models.Model): "Numbers and special characters are not allowed.") % field_label ) - @api.depends('age') + @api.depends('age', 'relationn') def _compute_get_age_status(self): + validation_setting = self.env["family.validation.setting"].search([], limit=1) + female_benefit_age = validation_setting.female_benefit_age or 26 + male_benefit_age = validation_setting.male_benefit_age or 18 + for rec in self: - current_education_status_id = rec.member_education_status_ids.filtered( - lambda r: r.education_status_type == 'current') - if rec.relationn.relation_type == 'son' and rec.age <= 18: - rec.age_status = 'minor' - elif rec.relationn.relation_type == 'daughter' and rec.age <= 26: - rec.age_status = 'minor' - elif rec.relationn.relation_type == 'son' and rec.age <= 22 and ( - current_education_status_id.specialization_ids.is_scientific_specialty or current_education_status_id.specialization_ids.is_medical_specialty): - rec.age_status = 'minor' + if not rec.relationn or not rec.relationn.relation_type: + rec.age_status = 'non_minor' + continue + + age = rec.age or 0 + rtype = rec.relationn.relation_type + + if rtype == 'daughter': + rec.age_status = 'minor' if age < female_benefit_age else 'non_minor' + elif rtype == 'son': + rec.age_status = 'minor' if age < male_benefit_age else 'non_minor' else: rec.age_status = 'non_minor' @@ -567,6 +603,8 @@ class FamilyMemberProfile(models.Model): 'member_location_conf', 'state', 'is_dead', + 'is_work', + 'case_study', 'benefit_id.member_ids.member_status', ) def check_member_status(self): @@ -577,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: @@ -610,6 +648,22 @@ class FamilyMemberProfile(models.Model): benefiting_children = rec.benefit_id.member_ids.filtered( lambda m: m.relationn.relation_type in ['son', 'daughter'] and m.member_status == 'benefit' ) + has_benefiting_son = any( + m.relationn.relation_type == 'son' and m.member_status == 'benefit' + for m in rec.benefit_id.member_ids + ) + + has_younger_benefiting_daughter = any( + m.relationn.relation_type == 'daughter' + and m.member_status == 'benefit' + and m.birth_date + and ( + m.birth_date + rd(years=female_benefit_age) + rd(days=(m.relationn.grace_period_days or 0)) + > date.today() + ) + for m in rec.benefit_id.member_ids + if m.id != rec.id + ) if rec.relationn.relation_type == 'mother': if not benefiting_children: @@ -631,45 +685,40 @@ class FamilyMemberProfile(models.Model): # Gender-specific checks elif rec.relationn.relation_type == 'son': if age_exceeded: - if rec.age <= exceptional_age_has_disabilities and rec.disabilities_attachment_ids and rec.minor_siblings: + if rec.age <= exceptional_age_has_disabilities and rec.disabilities_attachment_ids and ( + has_benefiting_son or has_younger_benefiting_daughter): rec.member_status = 'benefit' else: if rec.age > exceptional_age_has_disabilities and rec.disabilities_attachment_ids: rec.member_status = 'non_benefit' reasons.append( _("He has a physical or intellectual disability but is over %s years of age.") % exceptional_age_has_disabilities) - if current_education_status_id.case_study != 'continuous': + if rec.case_study != 'continuous': rec.member_status = 'non_benefit' reasons.append( _("Over %s years old and not enrolled in any educational institution.") % male_benefit_age ) - elif current_education_status_id.case_study == 'continuous': - if current_education_status_id.specialization_ids.is_scientific_specialty and rec.age >= exceptional_age_scientific_specialty: + elif rec.case_study == 'continuous': + if rec.age >= exceptional_age_scientific_specialty: rec.member_status = 'non_benefit' reasons.append( _("Over %s years old and not enrolled in a scientific or vocational specialization.") % exceptional_age_scientific_specialty ) - if not current_education_status_id.specialization_ids.is_scientific_specialty: - rec.member_status = 'non_benefit' - reasons.append( - _("The member is over %s years old and not enrolled in a scientific or vocational specialization.") - % male_benefit_age - ) - if rec.is_work: - if rec.member_income > max_income_for_benefit: - rec.member_status = 'non_benefit' - reasons.append(_("He is employed with a salary exceeding %s.") % max_income_for_benefit) - if not rec.is_married and rec.education_status in ['illiterate']: - rec.member_status = 'non_benefit' - reasons.append( - _("He is over %s years of age and not enrolled in an educational institution.") % male_benefit_age) - if not rec.is_married and rec.education_status in [ - 'educated'] and current_education_status_id.case_study in [ - 'graduate', 'intermittent']: - rec.member_status = 'non_benefit' - reasons.append( - _("He is over %s years of age and has completed his education.") % male_benefit_age) + if rec.is_work: + if rec.member_income > max_income_for_benefit: + rec.member_status = 'non_benefit' + reasons.append(_("He is employed with a salary exceeding %s.") % max_income_for_benefit) + if not rec.is_married and rec.education_status in ['illiterate']: + rec.member_status = 'non_benefit' + reasons.append( + _("He is over %s years of age and not enrolled in an educational institution.") % male_benefit_age) + if not rec.is_married and rec.education_status in [ + 'educated'] and rec.case_study in [ + 'graduate', 'intermittent']: + rec.member_status = 'non_benefit' + reasons.append( + _("He is over %s years of age and has completed his education.") % male_benefit_age) if not rec.member_location_conf.is_benefit: rec.member_status = 'non_benefit' reasons.append(_("He does not reside with his family.")) @@ -679,34 +728,31 @@ class FamilyMemberProfile(models.Model): _("Failure to complete the required documents or official proofs, or the family’s ineligibility for the association’s services, and the application has been rejected.")) elif rec.relationn.relation_type == 'daughter': - if rec.is_married: - rec.member_status = 'non_benefit' - reasons.append(_("Married")) if rec.age < female_benefit_age and rec.is_work and rec.education_status not in [ - 'educated'] and current_education_status_id.case_study != 'continuous': + 'educated'] and rec.case_study != 'continuous': rec.member_status = 'non_benefit' reasons.append(_("She is employed and not enrolled in an educational institution.")) if age_exceeded: - if rec.age > minor_siblings_age and not rec.minor_siblings: + if rec.age < minor_siblings_age and (has_benefiting_son or has_younger_benefiting_daughter): + rec.member_status = 'benefit' + else: rec.member_status = 'non_benefit' reasons.append( - _("She is over %s years of age and has no underage brothers.") % female_benefit_age) - elif not rec.minor_siblings: - rec.member_status = 'non_benefit' - reasons.append( - _("She is over %s years of age and has no underage brothers.") % female_benefit_age) - elif rec.minor_siblings and rec.age > minor_siblings_age: - rec.member_status = 'non_benefit' - reasons.append(_("She is over %s years of age.") % minor_siblings_age) - if rec.is_work and rec.member_income > max_income_for_benefit and rec.education_status in [ - 'educated'] and current_education_status_id.case_study == 'continuous': + _("She exceeded the age limit (%s) and has no benefiting son " + "nor any younger benefiting daughter.") + % female_benefit_age + ) + if rec.is_married: + rec.member_status = 'non_benefit' + reasons.append(_("Married")) + if rec.is_work and rec.member_income > max_income_for_benefit: rec.member_status = 'non_benefit' reasons.append(_("She works with a salary greater than %s.") % max_income_for_benefit) if rec.is_work and rec.education_status in ['illiterate']: rec.member_status = 'non_benefit' reasons.append(_("She is employed and not enrolled in an educational institution.")) if rec.is_work and rec.education_status in [ - 'educated'] and current_education_status_id.case_study in [ + 'educated'] and rec.case_study in [ 'graduate', 'intermittent']: rec.member_status = 'non_benefit' reasons.append(_("She is employed and has completed her education.")) @@ -828,14 +874,26 @@ class FamilyMemberProfile(models.Model): @api.depends('birth_date') def _compute_get_age_date(self): + today = date.today() for rec in self: - if rec.birth_date: - today = date.today() - day = datetime.strptime(str(rec.birth_date), DEFAULT_SERVER_DATE_FORMAT) - age = rd(today, day) - rec.age = age.years - else: + if not rec.birth_date: rec.age = 0 + rec.full_age = _("0 years, 0 months, 0 days") + continue + birth = fields.Date.from_string(rec.birth_date) + delta = rd(today, birth) + + years = delta.years + months = delta.months + days = delta.days + + rec.age = years + parts = [] + parts.append(_("%s year(s)") % years if years == 1 else _("%s years") % years) + parts.append(_("%s month(s)") % months if months == 1 else _("%s months") % months) + parts.append(_("%s day(s)") % days if days == 1 else _("%s days") % days) + + rec.full_age = ", ".join(parts) if parts else _("0 days") @api.onchange("member_id_number") def onchange_member_id_number(self): @@ -892,6 +950,11 @@ class FamilyMemberProfile(models.Model): # Member Suspend Manual def action_suspend(self): + ctx = dict(self.env.context or {}) + ctx.update({ + 'active_model': 'family.member', + 'active_id': self.id, + }) return { 'name': _('Suspend Reason Wizard'), 'view_mode': 'form', @@ -900,11 +963,17 @@ class FamilyMemberProfile(models.Model): 'res_model': 'suspend.reason.wizard', 'view_id': self.env.ref('odex_benefit.view_suspend_member_reason_wizard_form').id, 'target': 'new', + 'context': ctx, } def action_resume_member(self): ctx = dict(self.env.context or {}) - ctx['resume_family'] = True + # ctx['resume_family'] = True + ctx.update({ + 'resume_family': True, + 'active_model': 'family.member', + 'active_id': self.id, + }) return { 'name': _('Resume Reason Wizard'), 'view_mode': 'form', @@ -951,6 +1020,7 @@ class FamilyMemberProfile(models.Model): rec.state_a = 'family_services_manager' else: rec.state_a = 'second_approve' + rec.is_member_workflow = False rec.action_type = 'approved' def action_suspend_third_accept(self): @@ -963,6 +1033,7 @@ class FamilyMemberProfile(models.Model): def action_resume_third_accept(self): for rec in self: rec.state_a = 'second_approve' + rec.is_member_workflow = False rec.action_type = 'approved' def action_suspend_refuse(self): @@ -998,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 @@ -1136,28 +1207,28 @@ class FamilyMemberProfile(models.Model): for record in self: if not record.member_phone: continue - + phone = record.member_phone - + # Remove +966 prefix if present if phone.startswith('+966'): phone = phone[4:] record.member_phone = phone - + # Validate Saudi mobile pattern if re.match(SAUDI_MOBILE_PATTERN, phone) is None: raise ValidationError( _('Enter a valid Saudi mobile number')) - + # Check phone against family's main phones if record.benefit_id and phone in [ - record.benefit_id.phone, - record.benefit_id.phone2, + record.benefit_id.phone, + record.benefit_id.phone2, record.benefit_id.sms_phone ]: raise ValidationError( _("Phone number cannot be the same in The Family")) - + # Check for duplicate phone in other members (excluding current record) exist = self.search([ ('member_phone', '=', phone), @@ -1166,7 +1237,7 @@ class FamilyMemberProfile(models.Model): if exist: raise ValidationError( _("The phone Number already exists in Family with code %s") % exist.benefit_id.code) - + # Check if phone exists in grant.benefit exist_in_family = self.env["grant.benefit"].search([ '|', '|', diff --git a/odex25_benefit/odex_benefit/models/ir_attachment_inherit.py b/odex25_benefit/odex_benefit/models/ir_attachment_inherit.py index ed443109c..ce6457f33 100644 --- a/odex25_benefit/odex_benefit/models/ir_attachment_inherit.py +++ b/odex25_benefit/odex_benefit/models/ir_attachment_inherit.py @@ -18,7 +18,7 @@ class BenefitAttachment(models.Model): attach_id = fields.Many2one('attachments.settings', string="Attach",domain=[('attach_type', '=', 'member_attach')]) hobbies_id = fields.Many2one('attachments.settings', string="Hobby",domain=[('attach_type', '=', 'hobbies_attach')]) diseases_id = fields.Many2one('attachments.settings', string="Diseases",domain=[('attach_type', '=', 'diseases_attach'), ('parent_id','=',False)]) - diseases_child_id = fields.Many2one('attachments.settings',string="Sub Disease",domain="[('parent_id','=',diseases_id)]") + diseases_child_id = fields.Many2one('attachments.settings',string="Sub Disease",domain="[('attach_type', '=', 'diseases_attach'),('parent_id','=',diseases_id)]") disabilities_id = fields.Many2one('attachments.settings', string="Disabilities",domain=[('attach_type', '=', 'disabilities_attach'), ('parent_id','=',False)]) disabilities_child_id = fields.Many2one('attachments.settings',string="Sub Disability",domain="[('parent_id','=',disabilities_id)]") hobby_attach = fields.Binary(attachment=True, string="Hobby Attach") diff --git a/odex25_benefit/odex_benefit/models/service_request.py b/odex25_benefit/odex_benefit/models/service_request.py index 2c6c607b5..ff2b9856c 100644 --- a/odex25_benefit/odex_benefit/models/service_request.py +++ b/odex25_benefit/odex_benefit/models/service_request.py @@ -20,9 +20,10 @@ class ServiceRequest(models.Model): researcher_id = fields.Many2one("committees.line", string="Researcher", related="family_id.researcher_id", store=True) family_category = fields.Many2one('benefit.category', string='Family Category', - related='family_id.benefit_category_id', search="_search_benefit_category_id") + 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') + 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) + 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) @@ -79,7 +80,7 @@ class ServiceRequest(models.Model): start = fields.Date(string="Start Date") end = fields.Date(string='End Date') added_amount_if_mother_dead = fields.Float(string="Added Amount (If mother dead)", - compute="_get_added_amount_if_mother_dead",store=True) + compute="_get_added_amount_if_mother_dead", store=True) attachment_lines = fields.One2many( 'service.attachments.settings', 'service_request_id', @@ -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,9 +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) - - def _search_benefit_category_id(self, operator, value): - return [('family_id.benefit_category_id', operator, value)] + allowed_product_ids = fields.Many2many('product.product', string='Allowed Products', + compute='_compute_allowed_product_ids', store=True, ) def action_create_project(self): pass @@ -740,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: @@ -806,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 = {} @@ -968,31 +969,50 @@ 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) - domain = base_domain + [ - ('device_id', '=', rec.device_id.id), - ('date', '>', date_before) - ] - 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 - ) - allowed_qty = allowed_line.allowed_quantity if allowed_line else 0 - if total_qty > allowed_qty: + 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(_( - "You cannot request this device more than %s times within %s %s." - ) % (allowed_qty, interval, period)) + "Device '%s': Total quantity in this request (%s) exceeds the allowed limit of %s units." + ) % (device.device_name, current_total_qty, allowed_qty)) + if not rec.exception_or_steal: + domain = base_domain + [ + ('date', '>', date_before), + ('electrical_device_line_ids.device_id', '=', device_id), + ('exception_or_steal', '=', False) + ] + existing_requests = Service.search(domain) + existing_device_lines = existing_requests.mapped( + 'electrical_device_line_ids').filtered( + lambda l: l.device_id.id == device_id + ) + total_previous_qty = sum(existing_device_lines.mapped('quantity')) + total_qty = total_previous_qty + current_total_qty + + if total_qty > allowed_qty: + raise ValidationError(_( + "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: @@ -1060,7 +1080,7 @@ class ServiceRequest(models.Model): if service_type == 'rent': rec.service_max_amount += rec.added_amount_if_mother_dead rec.requested_service_amount = min(rec.rent_amount_payment, - rec.estimated_rent_amount_payment) + rec.added_amount_if_mother_dead + rec.estimated_rent_amount_payment + rec.added_amount_if_mother_dead) elif rec.max_limit_period == "calendar_year": current_date = rec.date.date() if isinstance(rec.date, datetime) else rec.date allowed_years = rec.service_cat.allowed_period or 1 @@ -1148,6 +1168,22 @@ class ServiceRequest(models.Model): domain = [('is_seasonal_service', '=', False), ('service_type', '!=', 'main_service'), ('benefit_category_ids', 'in', [rec.family_category.id])] + if rec.family_id: + mother_country = rec.family_id.mother_country_id + father_country = rec.family_id.father_country_id + + country_domain = [ + '|', + ('allowed_country_ids', '=', False), + '|', + ('allowed_country_ids', 'in', [mother_country.id] if mother_country else []), + ('allowed_country_ids', 'in', [father_country.id] if father_country else []) + ] + + allowed_services = self.env['services.settings'].search(country_domain) + + domain.append(('id', 'in', allowed_services.ids if allowed_services else [])) + if rec.family_id.property_type_id: if rec.family_id.property_type_code != 'ownership': domain.append(('service_type', '!=', 'home_restoration')) @@ -1160,6 +1196,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' @@ -1227,24 +1276,69 @@ class ServiceRequest(models.Model): "• Not be linked to any invoice" ) % 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, + invoice_lines = rec._prepare_invoice_lines() + if not invoice_lines: + raise UserError(_( + "Cannot create invoice for request '%s': No invoice lines could be created." + ) % rec.name) + 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': invoice_lines, + '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 _prepare_invoice_lines(self): + self.ensure_one() + line_ids = [] + if self.service_type == 'electrical_devices': + if not self.electrical_device_line_ids: + return line_ids + + for line in self.electrical_device_line_ids: + line_ids.append((0, 0, { + 'name': self.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': self._get_analytic_account_id(), + 'quantity': line.quantity, + 'price_unit': line.unit_price, + 'benefit_family_id': self.family_id.id, + })) + else: + description_parts = [] + if self.family_id and self.family_id.name: + description_parts.append(self.family_id.name) + if self.description: + description_parts.append(self.description) + if self.name: + description_parts.append(self.name) + + line_name = '/'.join(description_parts) if description_parts else _('Service') + line_ids.append((0, 0, { + 'name': line_name, + 'account_id': self.account_id.id or False, + 'analytic_account_id': self._get_analytic_account_id(), + 'quantity': self.service_qty or 1, + 'price_unit': self.requested_service_amount, + 'benefit_family_id': self.family_id.id, + })) + + return line_ids + + def _get_analytic_account_id(self): + self.ensure_one() + if (self.family_id.branch_family_id and + self.family_id.branch_family_id.branch and + self.family_id.branch_family_id.branch.analytic_account_id): + return self.family_id.branch_family_id.branch.analytic_account_id.id + return False def _get_total_move_lines(self): for rec in self: @@ -1343,7 +1437,7 @@ class ServiceRequest(models.Model): else: rec.rent_amount_payment = 0.0 - @api.depends('family_id','service_cat','payment_type') + @api.depends('family_id', 'service_cat', 'payment_type') def _get_added_amount_if_mother_dead(self): for rec in self: added_amount_if_mother_dead = 0.0 @@ -1351,4 +1445,4 @@ class ServiceRequest(models.Model): added_amount_if_mother_dead = rec.service_cat.raise_amount_for_orphan if rec.service_cat.raise_amount_for_orphan and rec.payment_type: added_amount_if_mother_dead = rec.service_cat.raise_amount_for_orphan / int(rec.payment_type) - rec.added_amount_if_mother_dead = added_amount_if_mother_dead \ No newline at end of file + rec.added_amount_if_mother_dead = added_amount_if_mother_dead diff --git a/odex25_benefit/odex_benefit/models/services_settings.py b/odex25_benefit/odex_benefit/models/services_settings.py index bcf29558e..0c381c6d2 100644 --- a/odex25_benefit/odex_benefit/models/services_settings.py +++ b/odex25_benefit/odex_benefit/models/services_settings.py @@ -145,6 +145,14 @@ class ServicesSettings(models.Model): domain="[('model_id.model', '=', 'family.member')]", help="Select fields from the Member profile to display only when the service is for a member." ) + allowed_country_ids = fields.Many2many( + comodel_name='res.country', + relation='services_settings_country_rel', + column1='service_id', + column2='country_id', + string='Allowed Countries', + help='Countries where this service is available. Leave empty to allow all countries.' + ) @api.onchange('benefit_type') def _onchange_benefit_type(self): @@ -203,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 06fa19693..5476cb3d2 100644 --- a/odex25_benefit/odex_benefit/security/ir.model.access.csv +++ b/odex25_benefit/odex_benefit/security/ir.model.access.csv @@ -1,8 +1,10 @@ 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_benefit_family_services_department_manager,access_benefit_family_services_department_manager,model_grant_benefit,odex_benefit.group_family_services_department_manager,1,1,1,1 access_education_status_all,education_status,model_education_status,odex_benefit.group_benefit_info,1,1,1,1 access_education_status_all_users,education_status_all_users,model_education_status,,1,0,0,0 access_weak_course_all,weak_course,model_weak_course,odex_benefit.group_benefit_info,1,1,1,1 @@ -125,6 +127,8 @@ access_suspend_reason,access_suspend_reason,model_suspend_reason,,1,0,0,0 access_suspend_reason_settings,access_suspend_reason_settings,model_suspend_reason,odex_benefit.group_benefit_settings,1,1,1,1 access_suspend_reason_wizard,access_suspend_reason_wizard,model_suspend_reason_wizard,,1,1,1,1 access_expense_family_services_manager,access_expense_family_services_manager,model_confirm_benefit_expense,odex_benefit.group_family_services_manager,1,1,1,1 +access_expense_family_services_department_manager,access_expense_family_services_department_manager,model_confirm_benefit_expense,odex_benefit.group_family_services_department_manager,1,1,1,1 + access_expense_benefit_manager,access_expense_benefit_manager,model_confirm_benefit_expense,odex_benefit.group_benefit_manager,1,1,1,1 access_expense_researcher,access_expense_researcher,model_confirm_benefit_expense,odex_benefit.group_benefit_info,1,0,0,0 access_branch_settings,access_branch_settings,model_branch_settings,base.group_user,1,0,0,0 @@ -153,6 +157,7 @@ access_exception_reason,access_exception_reason,model_exception_reason,base.grou access_exception_reason_settings,access_exception_reason_settings,model_exception_reason,odex_benefit.group_benefit_settings,1,1,1,1 access_service_request,access_service_request,model_service_request,base.group_user,1,1,1,1 access_seasonal_service_family_services_manager,access_seasonal_service_family_services_manager,model_seasonal_service,odex_benefit.group_family_services_manager,1,1,1,1 +access_seasonal_service_family_services_department_manager,access_seasonal_service_family_services_department_manager,model_seasonal_service,odex_benefit.group_family_services_department_manager,1,1,1,1 access_seasonal_service_benefit_manager,access_seasonal_service_benefit_manager,model_seasonal_service,odex_benefit.group_benefit_manager,1,1,1,1 access_seasonal_service_researcher,access_seasonal_service_researcher,model_seasonal_service,odex_benefit.group_benefit_info,1,0,0,0 access_marital_status,access_marital_status,model_marital_status,base.group_user,1,0,0,0 @@ -191,6 +196,7 @@ access_survey_user_input_line_group_benefit_info,survey.user_input.line.group_be 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_expense_line_family_services_manager,access_expense_line_family_services_manager,model_benefit_expense_line,odex_benefit.group_family_services_manager,1,1,1,1 +access_expense_line_family_services_department_manager,access_expense_line_family_services_department_manager,model_benefit_expense_line,odex_benefit.group_family_services_department_manager,1,1,1,1 access_expense_line_benefit_manager,access_expense_line_benefit_manager,model_benefit_expense_line,odex_benefit.group_benefit_manager,1,1,1,1 access_expense_researcher,access_expense_researcher,model_benefit_expense_line,odex_benefit.group_benefit_info,1,1,0,0 access_expense_line_payment_accountant_accept,access_expense_line_payment_accountant_accept,model_benefit_expense_line,odex_benefit.group_benefit_payment_accountant_accept,1,1,0,0 @@ -200,4 +206,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/security/security_view.xml b/odex25_benefit/odex_benefit/security/security_view.xml index f0b38f02f..e4d43313e 100644 --- a/odex25_benefit/odex_benefit/security/security_view.xml +++ b/odex25_benefit/odex_benefit/security/security_view.xml @@ -67,6 +67,12 @@ + + Family Member Exception + + + + Head of the Women's Committee @@ -107,7 +113,12 @@ Family Services Manager - + + + + Family Services Department Manager + + @@ -133,7 +144,7 @@ Show All Benefits Profiles [(1, '=', 1)] - + @@ -166,7 +177,7 @@ Show All Visit Location [(1, '=', 1)] - + @@ -215,7 +226,7 @@ Show All Family Complaints [(1, '=', 1)] - + @@ -295,7 +306,7 @@ Show All Family Members [(1, '=', 1)] - + @@ -339,7 +350,7 @@ Show All Bank Return Line [(1, '=', 1)] - + diff --git a/odex25_benefit/odex_benefit/views/actions_and_menus.xml b/odex25_benefit/odex_benefit/views/actions_and_menus.xml index 7ac943335..7a30a2ad5 100644 --- a/odex25_benefit/odex_benefit/views/actions_and_menus.xml +++ b/odex25_benefit/odex_benefit/views/actions_and_menus.xml @@ -564,7 +564,7 @@ '|', '&','&', ('is_seasonal','=',False), - ('state','=','accounting_approve'), + ('state','in',['accounting_approve','return_to_bank','family_received_device','send_request_to_supplier']), ('payment_order_state','=','none'), '&','&','&', ('is_seasonal','=',True), @@ -774,7 +774,7 @@ + web_icon="odex_benefit,static/description/icon1.png" groups="odex_benefit.group_benefit_info,odex_benefit.group_benefit_manager,odex_benefit.group_family_services_manager,odex_benefit.group_benefit_researcher,odex_benefit.group_benefit_woman_commitee,odex_benefit.group_benefit_branch_manager"/> + parent="odex_benefit.benefit_tools_services_settings_menu" action="services_settings_action" groups="odex_benefit.group_benefit_manager,odex_benefit.group_family_services_manager"/> + @@ -1016,9 +1017,13 @@ - + + + + + @@ -1033,6 +1038,7 @@ + @@ -1333,9 +1339,13 @@ - + + + + + @@ -1348,6 +1358,7 @@ + diff --git a/odex25_benefit/odex_benefit/views/benefit_view.xml b/odex25_benefit/odex_benefit/views/benefit_view.xml index 2733beaa1..5a6131ce0 100644 --- a/odex25_benefit/odex_benefit/views/benefit_view.xml +++ b/odex25_benefit/odex_benefit/views/benefit_view.xml @@ -362,29 +362,29 @@ grant.benefit.form grant.benefit -
+
diff --git a/odex25_benefit/odex_benefit/views/family_members.xml b/odex25_benefit/odex_benefit/views/family_members.xml index e62bd76b1..74e7ed486 100644 --- a/odex25_benefit/odex_benefit/views/family_members.xml +++ b/odex25_benefit/odex_benefit/views/family_members.xml @@ -4,7 +4,7 @@ family.member.form family.member - +
@@ -231,6 +234,7 @@ + - + @@ -389,14 +393,24 @@ - - + + + + + + + + + + + + + diff --git a/odex25_benefit/odex_benefit/views/services_settings.xml b/odex25_benefit/odex_benefit/views/services_settings.xml index 39a1dbc1d..71882e9c1 100644 --- a/odex25_benefit/odex_benefit/views/services_settings.xml +++ b/odex25_benefit/odex_benefit/views/services_settings.xml @@ -25,6 +25,7 @@ + @@ -170,6 +171,7 @@ + diff --git a/odex25_benefit/odex_benefit/wizards/suspend_reason_wizard.py b/odex25_benefit/odex_benefit/wizards/suspend_reason_wizard.py index 62aaa9693..8b0139416 100644 --- a/odex25_benefit/odex_benefit/wizards/suspend_reason_wizard.py +++ b/odex25_benefit/odex_benefit/wizards/suspend_reason_wizard.py @@ -81,6 +81,9 @@ class SuspendReasonWizard(models.TransientModel): action_type = ('resume_from_temporary' if benefit.state == 'temporary_suspended' else 'resume_from_final') + benefit.member_ids.write({ + 'action_type': action_type, + }) vals = { 'state': 'waiting_approve', 'action_type': action_type, @@ -97,6 +100,7 @@ class SuspendReasonWizard(models.TransientModel): 'is_excluded_suspension': False, 'is_member_workflow': False, 'final_suspend_date': rec.final_suspend_date, + 'action_type': 'suspended', }) if rec.suspend_attachment: attachment = rec._create_attachment_record_from_binary()