# -*- coding: utf-8 -*- import logging from datetime import datetime, date from dateutil.relativedelta import relativedelta as rd from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError from odoo.tools import DEFAULT_SERVER_DATE_FORMAT import qrcode import base64 from io import BytesIO from odoo.osv import expression SAUDI_MOBILE_PATTERN = "(^(05|5)(5|0|3|6|4|9|1|8|7)([0-9]{7})$)" ALPHABETIC_PATTERN = "^[\u0600-\u065F\u066A-\u06EF\u06FA-\u06FFA-Za-z ]+$" import re _logger = logging.getLogger(__name__) class FamilyMemberProfile(models.Model): _name = 'family.member' _description = "Member - Profiles" _inherit = ['mail.thread', 'mail.activity.mixin'] # _inherits = {'res.partner': 'partner_id'} _order = 'age desc' def _default_benefit(self): return self._context.get('active_id') member_first_name = fields.Char(string="Member First Name") member_second_name = fields.Char(string="Member Second Name", related="benefit_id.father_name") member_third_name = fields.Char(string="Member Third Name", related="benefit_id.father_second_name") member_family_name = fields.Char(string="Member Family Name", related="benefit_id.father_family_name") mother_first_name = fields.Char(string="Mother First Name") mother_second_name = fields.Char(string="Mother Second Name") mother_third_name = fields.Char(string="Mother Third Name") mother_family_name = fields.Char(string="MotherFamily Name") name = fields.Char(string="Name", compute='get_partner_name', store=True, readonly=False) member_id_number = fields.Char(string="Member Id Number") benefit_id = fields.Many2one("grant.benefit", string="Responsable", default=_default_benefit) branch_custom_id = fields.Many2one('branch.settings', string="Branch", related="benefit_id.branch_custom_id", search="_search_branch_custom_id") gender = fields.Selection(selection=[('male', 'Male'), ('female', 'Female')], string="Gender") member_phone = fields.Char(string="Member Phone") member_location = fields.Selection(selection=[('with_family', 'With Family'), ('with_relative', 'with a relative'), ('study_inside_saudi_arabia', 'Study Inside Saudi Arabia'), ('study_outside_saudi_arabia', 'Study Outside Saudi Arabia'), ('rehabilitation_center_for_the_disabled', 'Rehabilitation center for the disabled'), ('house_of_social_observations', 'House of Social Observations'), ('girls_home', 'Girls Home'), ('university_housing', 'University Housing'), ('with_husband', 'With_husband'), ('work_inside_saudi_arabia', 'Work Inside Saudi Arabia')], string="Member Location") member_location_conf = fields.Many2one('location.settings', string='Member Location') # 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) age_status = fields.Selection( [('minor', 'Minor'), ('non_minor', 'Non-Minor')], string='Age Status', default='non_minor', compute='_compute_get_age_status', store=True ) is_work = fields.Boolean('Is Work?') salary_certificate = fields.Many2many('ir.attachment', 'member_salary_cert_rel', 'member_id', 'attachment_id', string="Salary Certificate") is_dead = fields.Boolean('Is Dead?') death_certificate = fields.Many2many('ir.attachment', 'member_death_cert_rel', 'member_id', 'attachment_id', string="Death Certificate") member_income = fields.Float('Member Income') is_married = fields.Boolean('Is Married?') marriage_certificate = fields.Many2many('ir.attachment', 'member_marriage_cert_rel', 'member_id', 'attachment_id', string="Marriage Certificate") marriage_date = fields.Date(string='Marriage Contract Date') relationn = fields.Many2one('relation.settings', domain="['|',('relation_type','=','son'),('relation_type','=','daughter')]", string="Relation") relationn_type = fields.Selection(related="relationn.relation_type") relation = fields.Selection( [('son', _('Son')), ('daughter', _('Daughter'))]) mother_marital = fields.Selection( [('married', _('Married')), ('widower', _('Widower')), ('divorced', _('Divorced')), ('divorced_from_another_man', _('Divorced From Another Man')), ('prisoner', _('Prisoner')), ('dead', _('Dead')), ('hanging', _('Hanging'))], _('Marital Status')) mother_marital_conf = fields.Many2one('marital.status', string='Mother Marital') mother_location = fields.Selection( [('with_husband_and_children', _('With Husband And Children')), ('with_children', _('With Children')), ('not_live_with_children', _('Not live with children'))], string='Mother Location') attachment_ids = fields.One2many("ir.attachment", 'member_id', domain=[('hobbies_id', '=', False), ('diseases_id', '=', False), ('disabilities_id', '=', False)]) hobbies_attachment_ids = fields.One2many('ir.attachment', 'member_id', string='Hobbies Attachments', domain=[('hobbies_id', '!=', False)]) diseases_attachment_ids = fields.One2many('ir.attachment', 'member_id', string='Diseases Attachments', domain=[('diseases_id', '!=', False)]) disabilities_attachment_ids = fields.One2many('ir.attachment', 'member_id', string='Disabilities Attachments', domain=[('disabilities_id', '!=', False)]) hobbies_ids = fields.One2many("member.hobbies", 'member_id') diseases_ids = fields.One2many("member.diseases", 'member_id') disabilities_ids = fields.One2many("member.disabilities", 'member_id') exam_ids = fields.One2many("family.member.exam", 'member_id') is_scientific_specialty = fields.Boolean('Is Scientific Specialty?', related="specialization_ids.is_scientific_specialty") is_medical_specialty = fields.Boolean('Is Medical Specialty?', related="specialization_ids.is_medical_specialty") minor_siblings = fields.Boolean('minor siblings?', compute="_compute_minor_siblings", store=True) # Education_data member_education_status_ids = fields.One2many('education.status', 'family_member_id') education_status = fields.Selection( string='Education Status', selection=[('educated', 'educated'), ('illiterate', 'illiterate'), ('under_study_age', 'Under Study Age')], default='under_study_age', ) case_study = fields.Selection(string='Case Study', selection=[('continuous', 'continuous'), ('intermittent', 'intermittent'), ('graduate', 'Graduate')]) illiterate_reason = fields.Char(string='Illiterate Reason') intermittent_reason = fields.Many2one('education.illiterate.reason', string='Intermittent Reason') educational_certificate = fields.Binary(attachment=True, string='Educational Certificate') education_entity = fields.Selection(string='Education Entity', selection=[('governmental', 'Governmental'), ('special', 'Special')]) last_education_entity = fields.Selection(string='Last Education Entity', selection=[('governmental', 'Governmental'), ('special', 'Special')]) entities = fields.Many2one("res.partner", string='Entity') last_entities = fields.Many2one("res.partner", string='Last Entity') education_levels = fields.Many2one("education.level", string='Education Levels') last_education_levels = fields.Many2one("education.level", string='Last Education Levels') nearest_literacy_school = fields.Char(string='The Nearest Literacy School', required=False) literacy_school_note = fields.Text(string="Literacy School Note", required=False) classroom = fields.Many2one('education.classroom', string='Classroom') 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') percentage = fields.Float(string="Percentage%") last_percentage = fields.Float(string="Last Percentage%") education_start_date = fields.Date(string='Education Start Date') education_end_date = fields.Date(string='Education End Date') end_date = fields.Date('End Date') specialization_ids = fields.Many2one('specialization.specialization', string='specialization') last_specialization_ids = fields.Many2one('specialization.specialization', string='Last Specialization') last_education_start_date = fields.Date(string='Last Education Start Date') last_education_end_date = fields.Date(string='Last Education End Date') weak_study = fields.Many2many('study.material', string='Weak Study') is_want_education = fields.Boolean(string='is Want Education', required=False) is_quran_memorize = fields.Boolean('memorize the quran ?') partner_id = fields.Many2one('res.partner') # Replacement Mother add_replacement_mother = fields.Boolean('Add Replacement Mother?') # replacement_mother_name = fields.Char(string="Replacement Mother Name", tracking=True) # replacement_mother_second_name = fields.Char(string="Replacement Mother Second Name", tracking=True) # replacement_mother_third_name = fields.Char(string="Replacement Mother Third Name", tracking=True) # replacement_mother_family_name = fields.Char(string="Replacement Mother Family Name", tracking=True) # replacement_mother_country_id = fields.Many2one('res.country', 'Replacement Mother Nationality', tracking=True) # replacement_mother_id_number = fields.Char(string="Replacement Mother Id Number", tracking=True) # replacement_mother_marital_conf = fields.Many2one('marital.status', string='Replacement Mother Marital') # replacement_mother_location = fields.Selection( # [('with_husband_and_children', _('With Husband And Children')), ('with_children', _('With Children')), # ('not_live_with_children', _('Not live with children'))], string='Replacement Mother Location') # replacement_is_mother_work = fields.Boolean('Is Replacement Mother Work?') # replacement_mother_income = fields.Float("Replacement Mother Income") # replacement_mother_birth_date = fields.Date(string="Replacement Mother Birth Date") # replacement_mother_age = fields.Integer(string="Replacement Mother Age", # compute='_compute_get_replacement_mother_age') # replacement_mother_city_id = fields.Many2one('res.country.city', string='City') # replacement_mother_dead_reason = fields.Char(string='Dead Reason', required=False) # replacement_mother_dead_date = fields.Date(string="Certificate Date") # replacement_mother_dead_city_id = fields.Many2one('res.country.city', string='Dead City') # replacement_mother_status = fields.Selection(selection=[ # ('benefit', 'Benefit'), # ('non_benefit', 'Non Benefit'), # ], string='Replacement Mother Status', compute="check_replacement_mother_status", store=True, default=False) # # Education_data for replacement mother # replacement_education_status = fields.Selection(string='Education Status', # selection=[('educated', 'educated'), ('illiterate', 'illiterate')]) # replacement_case_study = fields.Selection(string='Mother Case Study', # selection=[('continuous', 'continuous'), ('intermittent', 'intermittent'), # ('graduate', 'Graduate')]) # replacement_illiterate_reason = fields.Char(string='Illiterate Reason') # replacement_intermittent_reason = fields.Many2one('education.illiterate.reason', # string='Intermittent Reason') # replacement_education_entity = fields.Selection(string='Education Entity', # selection=[('governmental', 'Governmental'), # ('special', 'Special')]) # replacement_specialization_ids = fields.Many2one('specialization.specialization', string='specialization') # replacement_classroom = fields.Many2one('education.classroom', string='Classroom') # replacement_degree = fields.Many2one('education.result', string='Degree') # replacement_percentage = fields.Float(string="Percentage%") # replacement_education_start_date = fields.Date(string='Education Start Date') # replacement_education_end_date = fields.Date(string='Education End Date') # # replacement_last_education_entity = fields.Selection(string='Last Education Entity', # selection=[('governmental', 'Governmental'), # ('special', 'Special')]) # replacement_last_entities = fields.Many2one("education.entities", string='Last Entity') # replacement_education_levels = fields.Many2one("education.level", string='Education Levels') # replacement_last_education_levels = fields.Many2one("education.level", string='Last Education Levels') # replacement_last_specialization_ids = fields.Many2one('specialization.specialization', string='Last Specialization') # # replacement_last_classroom = fields.Many2one('education.classroom', string='Last Classroom') # replacement_last_degree = fields.Many2one('education.result', string='Last Degree') # replacement_last_percentage = fields.Float(string="Last Percentage%") # replacement_last_education_start_date = fields.Date(string='Last Education Start Date') # replacement_last_education_end_date = fields.Date(string='Last Education End Date') # replacement_last_educational_certificate = fields.Binary(attachment=True, string='Last Educational Certificate') # replacement_weak_study = fields.Many2many('study.material', string='Weak Study') state = fields.Selection([ ('draft', 'Draft'), ('new', 'Researcher Assignment'), ('complete_info', 'Waiting for Researcher'), ('waiting_approve', 'Waiting for Operation Manager'), ('first_approve', 'Waiting for Branch Manager'), ('family_services_manager', 'Waiting Family Services Manager'), ('first_refusal', 'First Refusal'), ('second_approve', 'Second Approved'), ('refused', 'Refused'), ('temporary_suspended', 'Temporary Suspended'), ('suspended_second_approve', 'Suspended Second Approved'), ('exception_second_approve', 'Waiting for General Manager'), ('black_list', 'Black List'), ], string='state', tracking=True, compute='_get_state', store=True, group_expand='_expand_states') state_a = fields.Selection([ ('draft', 'Draft'), ('new', 'Researcher Assignment'), ('complete_info', 'Waiting for Researcher'), ('waiting_approve', 'Waiting for Operation Manager'), ('first_approve', 'Waiting for Branch Manager'), ('family_services_manager', 'Waiting Family Services Manager'), ('first_refusal', 'First Refusal'), ('second_approve', 'Second Approved'), ('refused', 'Refused'), ('temporary_suspended', 'Temporary Suspended'), ('suspended_second_approve', 'Suspended Second Approved'), ('exception_second_approve', 'Waiting for General Manager'), ('black_list', 'Black List'), ], string='stateA', default="draft", tracking=True) action_type = fields.Selection(selection=[ ('new', 'New'), ('edit_info', 'Edit Information'), ('approved', 'Approved'), ('suspended', 'Suspended'), ('resume_from_temporary', 'Resume from Temporary Suspension'), ('resume_from_final', 'Resume from Final Suspension'), ('exception', 'Exception'), ], string='Action Type', default='new', tracking=True) member_status = fields.Selection(selection=[ ('benefit', 'Benefit'), ('non_benefit', 'Non Benefit'), ], string='Benefit Status', compute="check_member_status", default=False, store=True) suspend_reason = fields.Many2one('suspend.reason', string='Suspend Reason') reason = fields.Text(string='Reason') suspend_description = fields.Text(string='Suspend Description') suspend_attachment = fields.Many2many('ir.attachment', 'rel_suspend_member_attachment', 'member_id', 'attachment_id', string='Suspend Attachment') suspend_type = fields.Selection( selection=[('temporarily_suspend', 'Temporarily Suspended'), ('suspend', 'Suspend')], string="Suspend Type") suspend_method = fields.Selection(selection=[('manual', 'Manual'), ('auto', 'Auto')], string="Suspend Method", default='auto') is_member_workflow = fields.Boolean('Is Member Workflow?') # sponsor_id = fields.Many2one('res.partner', string='Sponsor Partner',domain="[('account_type','=','sponsor')]") sponsor_id = fields.Many2one('res.partner', string='Sponsor Partner', domain="[('is_sponsor_portal', '=', True)]") sponsor_related_id = fields.Many2one('res.partner', string='Sponsor') sponsorship_id = fields.Many2one('takaful.sponsorship', string='Sponsorship') required_attach = fields.Selection(selection=[('true', 'True'), ('false', 'False')], compute='get_required_attach', store=True, string='Member Required Attach') # Exception fields exception_reason = fields.Many2one('exception.reason', string='Exception Reason') exception_description = fields.Text(string='Exception Description') exception_type = fields.Selection( selection=[('temporarily_exception', 'Temporarily Exception'), ('permanent_exception', 'Permanent Exception')], string="Exception Type") exception_attachment = fields.Binary(string='Exception Attachment', attachment=True) exception_start_date = fields.Datetime(string='Exception Start Date') exception_end_date = fields.Datetime(string='Exception End Date') is_excluded_suspension = fields.Boolean('Excluded from suspension?') is_mother = fields.Boolean('Is Mother?') total_member_service_requests = fields.Integer(compute='_get_total_member_service_requests') non_benefit_reason = fields.Text(string="Non Benefit Reason", tracking=True) final_suspend_date = fields.Date(string="Final Suspend Date") active = fields.Boolean(string='Active', default=True) resume_reason_id = fields.Many2one('suspend.reason', string='Return Reason') resume_date = fields.Date(string="Return Date") resume_notes = fields.Text(string="Return Notes") exit_benefit_date = fields.Date(string="Exit Benefit Date") # def create(self, vals): # for line_vals in vals: # if line_vals.get("education_status", False) == "educated" and "member_education_status_ids" not in line_vals: # raise ValidationError( # _("You should at least insert one current/previous education status!") # ) # return super(FamilyMemberProfile, self).create(vals) # def write(self, vals): # education_status = vals.get("education_status", False) or self.education_status # if education_status == "educated" and "member_education_status_ids" not in vals and not len(self.member_education_status_ids): # raise ValidationError( # _("You should at least insert one current/previous education status!") # ) # return super(FamilyMemberProfile, self).write(vals) def _expand_states(self, states, domain, order): return [key for key, val in type(self).state.selection] def _search_branch_custom_id(self, operator, value): return [('benefit_id.branch_custom_id', operator, value)] @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): if self._context.get('members_domain_force_all'): self = self.sudo() return super(FamilyMemberProfile, self).name_search(name, args, operator, limit) @api.model def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): if not args: args = [] if name: args = expression.AND([args, ['|', ('name', operator, name), ('member_phone', operator, name)]]) return self._search(args, limit=limit, access_rights_uid=name_get_uid) @api.onchange('age') def _check_son_daughter_age(self): for rec in self: if rec.relationn.relation_type in ['son', 'daughter'] and rec.relationn.age_difference > 0: if rec.benefit_id.father_age and rec.benefit_id.father_age - rec.age < rec.relationn.age_difference: raise ValidationError( _("The son/daughter's age is supposed to be less than the father's age by %s") % rec.relationn.age_difference ) if rec.benefit_id.mother_age and rec.benefit_id.mother_age - rec.age < rec.relationn.age_difference: raise ValidationError( _("The son/daughter's age is supposed to be less than the mother's age by %s") % rec.relationn.age_difference ) @api.depends('age_status', 'is_dead', 'benefit_id.member_ids.age_status', 'benefit_id.member_ids.is_dead') def _compute_minor_siblings(self): for member in self: member.minor_siblings = any( m.age_status == 'minor' and m.member_status == 'benefit' for m in member.benefit_id.member_ids ) @api.constrains('is_dead', 'death_certificate') def _check_death_certificate_required(self): for rec in self: if rec.is_dead and not rec.death_certificate: raise ValidationError(_( "Death Certificate is required for member '%s' when marked as deceased." ) % rec.name) # @api.constrains('is_work', 'age', 'member_income', 'salary_certificate') # def _check_salary_certificate_required(self): # for rec in self: # if rec.age >= 18 and rec.is_work: # if rec.member_income <= 0: # raise ValidationError(_( # "Income must be greater than 0 for member '%s' when working and age is greater than or equal to 18." # ) % rec.name) # if not rec.salary_certificate: # raise ValidationError(_( # "Salary Certificate is required for member '%s' when working and age is greater than or equal to 18." # ) % rec.name) # # @api.constrains('is_mother', 'is_married', 'marriage_certificate') # def _check_marriage_certificate_required(self): # for rec in self: # if not rec.is_mother and rec.is_married and not rec.marriage_certificate: # raise ValidationError(_( # "Marriage Certificate is required for member '%s' when marked as married and not a mother." # ) % rec.name) @api.constrains('member_first_name') def _check_names_alphabetic(self): char_fields = { 'member_first_name': _('Member First Name'), } for record in self: for field_name, field_label in char_fields.items(): field_value = getattr(record, field_name) if field_value and not re.match(ALPHABETIC_PATTERN, field_value.strip()): raise ValidationError( _("%s must contain only alphabetic characters and spaces. " "Numbers and special characters are not allowed.") % field_label ) @api.depends('age') def _compute_get_age_status(self): 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' else: rec.age_status = 'non_minor' @api.constrains('birth_date') def _check_dates(self): today = date.today() for rec in self: # Check that dates are not in the future if rec.birth_date and rec.birth_date > today: raise ValidationError("Member's Birth Date cannot be in the future.") def unlink(self): for order in self: if order.state not in ['draft']: raise UserError(_('You cannot delete this record')) self.env['ir.attachment'].search([('member_id', '=', order.id)]).unlink() if order.hobbies_ids: order.hobbies_ids.unlink() if order.diseases_ids: order.diseases_ids.unlink() if order.disabilities_ids: order.disabilities_ids.unlink() if order.member_education_status_ids: order.member_education_status_ids.unlink() return super(FamilyMemberProfile, self).unlink() @api.depends('is_member_workflow', 'benefit_id.state', 'state_a') def _get_state(self): for rec in self: if not rec.is_member_workflow: rec.state = rec.benefit_id.state else: rec.state = rec.state_a @api.depends('member_first_name', 'member_second_name', 'member_third_name', 'member_family_name') def get_partner_name(self): for rec in self: rec.name = '' if all([rec.member_second_name, rec.member_first_name, rec.member_third_name, rec.member_family_name]): rec.name = rec.member_first_name + " " + rec.member_second_name + " " + rec.member_third_name + " " + rec.member_family_name elif all([rec.mother_second_name, rec.mother_first_name, rec.mother_third_name, rec.mother_family_name]): rec.name = rec.mother_first_name + " " + rec.mother_second_name + " " + rec.mother_third_name + " " + rec.mother_family_name else: rec.name = '' @api.model def default_get(self, fields): res = super(FamilyMemberProfile, self).default_get(fields) # Get default attachments default_attachment = self.env["attachments.settings"].search([('is_default', '=', True)]) # Prepare the list of default attachments for the one2many field default_attachments_data = [] for attach in default_attachment: if attach.attach_type == 'member_attach': if not attach.name: # Ensure name is set raise ValidationError('The attachment name is missing.') default_attachments_data.append((0, 0, { 'name': attach.name, 'attach_id': attach.id, 'is_required': attach.is_required, 'is_default': attach.is_default, 'show_in_portal': attach.show_in_portal })) # Add the default attachments to the res dictionary for attachment_ids if 'attachment_ids' in fields: res['attachment_ids'] = default_attachments_data return res def _get_total_member_service_requests(self): for rec in self: rec.total_member_service_requests = self.env['service.request'].search_count( [('benefit_type', '=', 'member'), ('member_id', '=', rec.id)]) def action_open_related_member_service_requests(self): """ Opens a tree view with related records filtered by a dynamic domain """ member_service_requests = self.env['service.request'].search([ ('benefit_type', '=', 'member'), ('member_id', '=', self.id) ]).ids action = { 'type': 'ir.actions.act_window', 'name': _('Service Requests'), 'res_model': 'service.request', 'view_mode': 'tree,form', 'domain': [('id', 'in', member_service_requests)], 'target': 'current', } return action @api.depends('attachment_ids') def get_required_attach(self): for rec in self.attachment_ids: if rec.is_required and not rec.datas: self.required_attach = None break elif rec.is_required and rec.datas: self.required_attach = 'true' elif rec.is_default and not rec.is_required and (rec.datas or not rec.datas): self.required_attach = 'true' else: self.required_attach = 'true' # def create_member_partner(self): # self.partner_id.write({ # 'email': self.email, # 'phone': self.phone, # 'account_type': 'benefit', # 'is_benefit': True, # 'code': self.benefit_id.code, # }) @api.depends( 'member_education_status_ids', 'member_education_status_ids.specialization_ids', 'member_education_status_ids.education_status', 'member_education_status_ids.case_study', 'relationn', 'birth_date', 'is_married', 'minor_siblings', 'member_income', 'is_married', 'member_location_conf', 'state', 'is_dead', 'benefit_id.member_ids.member_status', ) def check_member_status(self): for rec in self: reasons = [] rec.non_benefit_reason = False current_education_status_id = rec.member_education_status_ids.filtered( lambda r: r.education_status_type == 'current') if rec.state == 'second_approve' and rec.is_excluded_suspension: rec.member_status = 'benefit' continue if rec.birth_date: validation_setting = self.env["family.validation.setting"].search([], limit=1) female_benefit_age = validation_setting.female_benefit_age male_benefit_age = validation_setting.male_benefit_age exceptional_age_scientific_specialty = validation_setting.exceptional_age_scientific_specialty exceptional_age_medical_specialty = validation_setting.exceptional_age_medical_specialty exceptional_age_has_disabilities = validation_setting.exceptional_age_has_disabilities minor_siblings_age = validation_setting.minor_siblings_age max_income_for_benefit = validation_setting.max_income_for_benefit grace_days = rec.relationn.grace_period_days or 0 if rec.relationn.relation_type == 'daughter': limit_age = female_benefit_age elif rec.relationn.relation_type == 'son': limit_age = male_benefit_age else: limit_age = 0 if limit_age > 0: start_of_next_year = rec.birth_date + rd(years=limit_age) end_benefit_date = start_of_next_year + rd(days=grace_days) age_exceeded = date.today() > end_benefit_date else: age_exceeded = False rec.member_status = 'benefit' # Default to benefit benefiting_children = rec.benefit_id.member_ids.filtered( lambda m: m.relationn.relation_type in ['son', 'daughter'] and m.member_status == 'benefit' ) if rec.relationn.relation_type == 'mother': if not benefiting_children: rec.write({'member_status': 'non_benefit'}) reasons.append(_("Mother has no benefiting children")) else: rec.member_status, mother_reasons = rec.benefit_id.check_mother_status() reasons.append(mother_reasons) elif rec.relationn.relation_type == 'replacement_mother': rec.member_status, repl_reasons = rec.benefit_id.check_replacement_mother_status() reasons.append(repl_reasons) if rec.state in ['suspended_second_approve', 'refused'] or not benefiting_children: rec.member_status = 'non_benefit' reasons.append( _("The application has been rejected due to missing required documents, lack of official proofs, or the family's ineligibility for the association's services.")) # continue # Skip further checks for mothers # 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: 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': 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: 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 not rec.member_location_conf.is_benefit: rec.member_status = 'non_benefit' reasons.append(_("He does not reside with his family.")) if rec.state in ['suspended_second_approve', 'refused'] or rec.is_dead == True: rec.member_status = 'non_benefit' reasons.append( _("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': rec.member_status = 'non_benefit' reasons.append(_("She is employed and not enrolled in an educational institution.")) if age_exceeded: rec.member_status = 'non_benefit' reasons.append(_("She has exceeded the maximum age limit of %s years.") % female_benefit_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': 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 [ 'graduate', 'intermittent']: rec.member_status = 'non_benefit' reasons.append(_("She is employed and has completed her education.")) if not rec.member_location_conf.is_benefit: rec.member_status = 'non_benefit' reasons.append(_("She does not reside with the family.")) if rec.state in ['suspended_second_approve', 'refused'] or rec.is_dead == True: rec.member_status = 'non_benefit' reasons.append( _("Application rejected due to missing documents, missing official proofs, or the family's ineligibility for the association's services.")) # General checks for all members # if rec.is_work: # if rec.member_income > max_income_for_benefit: # rec.member_status = 'non_benefit' # if not rec.is_married and current_education_status_id.education_status in ['illiterate'] and current_education_status_id.case_study in [ # 'graduate', 'intermittent']: # rec.member_status = 'non_benefit' # if rec.member_location in ['with_relative', 'study_outside_saudi_arabia']: # rec.member_status = 'non_benefit' else: rec.member_status = False if rec.member_status == 'non_benefit': reasons = [str(r) for r in reasons if r] rec.non_benefit_reason = "\n".join(reasons) if not rec.exit_benefit_date: rec.exit_benefit_date = fields.Date.today() elif rec.member_status == 'benefit': rec.exit_benefit_date = False # @api.depends('relationn','birth_date', 'is_scientific_specialty', 'is_medical_specialty', 'has_disabilities', 'is_married', # 'minor_siblings','member_income','is_married','member_location_conf','education_status','case_study','state','is_dead') # def check_member_status(self): # for rec in self: # if rec.state == 'second_approve' and rec.is_excluded_suspension: # rec.member_status = 'benefit' # continue # if rec.birth_date: # validation_setting = self.env["family.validation.setting"].search([], limit=1) # female_benefit_age = validation_setting.female_benefit_age # male_benefit_age = validation_setting.male_benefit_age # exceptional_age_scientific_specialty = validation_setting.exceptional_age_scientific_specialty # exceptional_age_medical_specialty = validation_setting.exceptional_age_medical_specialty # exceptional_age_has_disabilities = validation_setting.exceptional_age_has_disabilities # minor_siblings_age = validation_setting.minor_siblings_age # max_income_for_benefit = validation_setting.max_income_for_benefit # rec.member_status = 'benefit' # Default to benefit # if rec.relationn.relation_type == 'mother': # rec.member_status = rec.benefit_id.mother_status # if rec.relationn.relation_type == 'replacement_mother': # rec.member_status = rec.benefit_id.replacement_mother_status # if rec.state == 'suspended_second_approve': # rec.member_status = 'non_benefit' # # continue # Skip further checks for mothers # # Gender-specific checks # if rec.relationn.relation_type == 'son': # if rec.age > male_benefit_age: # if rec.has_disabilities and rec.age > exceptional_age_has_disabilities: # rec.member_status = 'non_benefit' # elif rec.is_scientific_specialty and rec.age > exceptional_age_scientific_specialty and not rec.has_disabilities and not rec.minor_siblings : # rec.member_status = 'non_benefit' # elif rec.is_medical_specialty and rec.age > exceptional_age_medical_specialty and not rec.has_disabilities and not rec.minor_siblings: # rec.member_status = 'non_benefit' # elif not any([rec.is_scientific_specialty, rec.is_medical_specialty, rec.has_disabilities]): # rec.member_status = 'non_benefit' # if rec.is_work: # if rec.member_income > max_income_for_benefit: # rec.member_status = 'non_benefit' # if not rec.is_married and rec.education_status in ['illiterate']: # rec.member_status = 'non_benefit' # if not rec.is_married and rec.education_status in ['educated'] and rec.case_study in [ # 'graduate', 'intermittent']: # rec.member_status = 'non_benefit' # if not rec.member_location_conf.is_benefit: # rec.member_status = 'non_benefit' # if rec.state == 'suspended_second_approve' or rec.is_dead == True: # rec.member_status = 'non_benefit' # elif rec.relationn.relation_type == 'daughter': # if rec.age < female_benefit_age and rec.is_married: # rec.member_status = 'non_benefit' # if rec.age < female_benefit_age and rec.is_work and rec.education_status not in ['educated'] and rec.case_study != 'continuous': # rec.member_status = 'non_benefit' # if rec.age > female_benefit_age: # if rec.age > minor_siblings_age and not rec.minor_siblings: # rec.member_status = 'non_benefit' # elif not rec.minor_siblings: # rec.member_status = 'non_benefit' # elif rec.minor_siblings and rec.age > minor_siblings_age: # rec.member_status = 'non_benefit' # # elif rec.is_work and rec.education_status in ['illiterate'] and rec.case_study in [ # # 'graduate', 'intermittent']: # # rec.member_status = 'non_benefit' # elif rec.is_married: # rec.member_status = 'non_benefit' # # elif not rec.minor_siblings: # # rec.member_status = 'non_benefit' # if rec.is_work and rec.member_income > max_income_for_benefit and rec.education_status in ['educated'] and rec.case_study == 'continuous': # rec.member_status = 'non_benefit' # if rec.is_work and rec.education_status in ['illiterate'] : # rec.member_status = 'non_benefit' # if rec.is_work and rec.education_status in ['educated'] and rec.case_study in [ # 'graduate', 'intermittent']: # rec.member_status = 'non_benefit' # if not rec.member_location_conf.is_benefit: # rec.member_status = 'non_benefit' # if rec.state == 'suspended_second_approve' or rec.is_dead == True: # rec.member_status = 'non_benefit' # # General checks for all members # # if rec.is_work: # # if rec.member_income > max_income_for_benefit: # # rec.member_status = 'non_benefit' # # if not rec.is_married and rec.education_status in ['illiterate'] and rec.case_study in [ # # 'graduate', 'intermittent']: # # rec.member_status = 'non_benefit' # # if rec.member_location in ['with_relative', 'study_outside_saudi_arabia']: # # rec.member_status = 'non_benefit' # else: # rec.member_status = False @api.depends('birth_date') def _compute_get_age_date(self): 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: rec.age = 0 @api.onchange("member_id_number") def onchange_member_id_number(self): for rec in self: if rec.member_id_number: if not rec.member_id_number.isdigit(): raise ValidationError(_("The ID number should contain only digits.")) if len(rec.member_id_number) != 10: raise ValidationError(_("The ID number must contain exactly 10 digits.")) family = rec.benefit_id if rec.relationn.relation_type in ['mother', 'replacement_mother']: allowed_ids = [family.mother_id_number, family.replacement_mother_id_number] if rec.member_id_number in allowed_ids: continue if rec.member_id_number in [family.father_id_number, family.mother_id_number, family.replacement_mother_id_number]: raise ValidationError( _("Member ID %s cannot match father/mother/replacement mother ID.") % rec.member_id_number ) existing_member = rec.search([ ('id', '!=', rec._origin.id), ('member_id_number', '=', rec.member_id_number), ('relationn.relation_type', 'not in', ['mother', 'replacement_mother']) ], limit=1) if existing_member: raise ValidationError(_("This ID already exists.")) if rec.relationn.relation_type not in ['mother', 'replacement_mother']: conflict_family = rec.env['grant.benefit'].search([ ('id', '!=', family._origin.id), '|', '|', ('father_id_number', '=', rec.member_id_number), ('mother_id_number', '=', rec.member_id_number), ('replacement_mother_id_number', '=', rec.member_id_number), ], limit=1) if conflict_family: raise ValidationError( _("Member ID %s already exists in another family (code %s).") % (rec.member_id_number, conflict_family.code) ) @api.onchange('relationn', 'member_status', 'gender', 'birth_date', 'is_scientific_specialty', 'is_medical_specialty', 'is_married', 'minor_siblings', 'member_income', 'is_married', 'member_location_conf', 'education_status', 'case_study') def onchange_member_status(self): res = {} for rec in self: if rec.member_status == 'non_benefit': res['warning'] = {'title': _('ValidationError'), 'message': _('Not Benefit')} return res # Member Suspend Manual def action_suspend(self): return { 'name': _('Suspend Reason Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'suspend.reason.wizard', 'view_id': self.env.ref('odex_benefit.view_suspend_member_reason_wizard_form').id, 'target': 'new', } def action_resume_member(self): ctx = dict(self.env.context or {}) ctx['resume_family'] = True return { 'name': _('Resume Reason Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'suspend.reason.wizard', 'view_id': self.env.ref('odex_benefit.view_resume_reason_wizard_form').id, 'target': 'new', 'context': ctx, } def action_final_suspend(self): today = fields.Date.today() for rec in self: if rec.final_suspend_date and today >= rec.final_suspend_date: rec.write({ 'state': 'suspended_second_approve', 'suspend_type': 'suspend', }) else: raise UserError( _("Final suspension cannot be executed until after the specified final suspension date.")) def action_suspend_first_accept(self): for rec in self: rec.state_a = 'first_approve' def action_resume_first_accept(self): for rec in self: rec.state_a = 'first_approve' def action_suspend_second_accept(self): for rec in self: if rec.suspend_reason.need_service_manager_approval: rec.state_a = 'family_services_manager' elif rec.suspend_reason.suspend_type == 'temporarily_suspend': rec.state_a = 'temporary_suspended' else: rec.state_a = 'suspended_second_approve' def action_resume_second_accept(self): for rec in self: if rec.suspend_reason.need_service_manager_approval: rec.state_a = 'family_services_manager' else: rec.state_a = 'second_approve' rec.action_type = 'approved' def action_suspend_third_accept(self): for rec in self: if rec.suspend_reason.suspend_type == 'temporarily_suspend': rec.state_a = 'temporary_suspended' else: rec.state_a = 'suspended_second_approve' def action_resume_third_accept(self): for rec in self: rec.state_a = 'second_approve' rec.action_type = 'approved' def action_suspend_refuse(self): for rec in self: rec.state_a = 'second_approve' rec.is_member_workflow = False def action_reject_resume(self): for rec in self: if rec.action_type == 'resume_from_temporary': rec.state = 'temporary_suspended' elif rec.action_type == 'resume_from_final': rec.state = 'suspended_second_approve' rec.action_type = 'suspended' # Excption Work flow def action_exception(self): for rec in self: rec.is_member_workflow = True return { 'name': _('Exception Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'exception.wizard', 'view_id': self.env.ref('odex_benefit.view_exception_member_wizard_form').id, 'target': 'new', } def action_exception_first_accept(self): for rec in self: rec.state_a = 'first_approve' def action_exception_second_accept(self): for rec in self: rec.is_excluded_suspension = True rec.state_a = 'exception_second_approve' # rec.is_member_workflow = False def action_exception_final_accept(self): for rec in self: rec.is_excluded_suspension = True rec.state_a = 'second_approve' rec.is_member_workflow = False def action_auto_exception(self): obj = self.env["family.member"].search( [('state', '=', 'second_approve'), ('is_excluded_suspension', '=', True)]) for rec in obj: if rec.exception_end_date and rec.exception_end_date <= fields.Datetime.now(): rec.is_excluded_suspension = False rec.state = 'suspended_second_approve' def run_auto_final_suspend(self): today = date.today() records = self.search([ ('state', '=', 'temporary_suspended'), ('final_suspend_date', '<=', today), ]) for rec in records: rec.state = 'suspended_second_approve' def action_exception_refuse(self): for rec in self: rec.state_a = 'suspended_second_approve' # Methods for Work flow for Member def complete_data(self): # message = self.create_message('complete_info') # self.partner_id.send_sms_notification(message, self.phone) self.state_a = 'complete_info' return { 'name': _('Rerearcher Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'researcher.member.wizard', 'view_id': self.env.ref('odex_benefit.view_resarcher_member_wizard_form').id, 'target': 'new', } def finish_complete_data(self): message = self.create_message('waiting_approve') # self.partner_id.send_sms_notification(message, self.phone) self.state_a = 'waiting_approve' def action_accepted(self): """Accept registration""" self.state_a = "second_approve" def action_first_refusal(self): """First refusal to entity registration""" domain = [] context = {} context = dict(self.env.context or {}) context['state_a'] = "first_refusal" # self.partner_id.send_sms_notification("First Refusal", self.phone) context['active_id'] = self.id return { 'name': _('Refuse Reason Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'entity.refused.reason.wizard', 'view_id': self.env.ref('odex_benefit.view_entity_refused_reason_wizard_form').id, 'target': 'new', 'domain': domain, 'context': context, } def action_refuse(self): """Refuse entity registration""" domain = [] context = dict(self.env.context or {}) context['state'] = "refused" context['active_id'] = self.id return { 'name': _('Refuse Reason Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'entity.refused.reason.wizard', 'view_id': self.env.ref('odex_benefit.view_entity_final_refused_reason_wizard_form').id, 'target': 'new', 'domain': domain, 'context': context, } def action_black_list(self): """Move benefit to black list""" domain = [] context = dict(self.env.context or {}) context['state'] = "black_list" context['active_id'] = self.id return { 'name': _('Black List Wizard'), 'view_mode': 'form', 'view_type': 'form', 'type': 'ir.actions.act_window', 'res_model': 'entity.black.list.wizard', 'view_id': self.env.ref('odex_benefit.view_entity_black_list_wizard_form').id, 'target': 'new', 'domain': domain, 'context': context, } def action_edit_info(self): self.state = 'complete_info' def action_finish_edit(self): for rec in self: # group_e = self.env.ref('odex_benefit.group_benefit_edit', False) # group_e.write({'users': [(3, self.user_id.id)]}) rec.state_a = rec.old_stage def create_manual_visit(self): self.env['visit.location'].create({ 'benefit_id': self.id, 'visit_date': date.today(), 'visit_types': 2, # 'selector': 'researcher', 'researcher_id': self.researcher_id.id, # 'researcher_team': rec.researcher_team.id, 'state': 'draft' }) @api.constrains('member_phone') def _onchange_member_phone_validation(self): if self.member_phone: if self.member_phone.startswith('+966'): member_phone = self.member_phone[4:] self.member_phone = member_phone if re.match(SAUDI_MOBILE_PATTERN, self.member_phone) == None: raise ValidationError( _('Enter a valid Saudi mobile number')) exist = self.search([('member_phone', '=', self.member_phone)]) if exist: raise ValidationError( _('This Phone Already Exist!')) # Check if the father ID and mother ID are the same on the same record if self.member_phone == self.benefit_id.phone or self.member_phone == self.benefit_id.phone2 or self.member_phone == self.benefit_id.sms_phone: raise ValidationError( _("Phone number cannot be the same in The Family")) # Check if the ID number exists in other records or in family members exist = self.search([ ('member_phone', '=', self.member_phone) ], limit=1) exist_in_family = self.env["grant.benefit"].search([ '|', '|', ('phone', '=', self.member_phone), ('phone2', '=', self.member_phone), ('sms_phone', '=', self.member_phone), ], limit=1) if exist or exist_in_family: if exist_in_family: raise ValidationError( _("The phone Number already exists in Family with code %s") % exist_in_family.code) if exist: raise ValidationError( _("The phone Number already exists in Family with code %s") % exist.benefit_id.code)