odex25_standard/odex25_benefit/odex_benefit/models/family_members.py

1231 lines
64 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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)
benefit_code = fields.Char(string='Family Code', related='benefit_id.code', store=True)
branch_custom_id = fields.Many2one('branch.settings', string="Branch", related="benefit_id.branch_custom_id",
search="_search_branch_custom_id")
researcher_id = fields.Many2one("committees.line", string="Researcher", related="benefit_id.researcher_id",
search="_search_researcher_id")
benefit_category_id = fields.Many2one('benefit.category', string='Benefit Category',
related="benefit_id.benefit_category_id",
search="_search_benefit_category_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')], compute='_compute_last_education_data',
store=True)
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',
compute='_compute_last_education_data', store=True)
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")
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:
# 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)]
def _search_researcher_id(self, operator, value):
return [('benefit_id.researcher_id', operator, value)]
def _search_benefit_category_id(self, operator, value):
return [('benefit_id.benefit_category_id', operator, value)]
@api.depends('member_education_status_ids',
'member_education_status_ids.education_levels',
'member_education_status_ids.case_study',
'member_education_status_ids.education_start_date')
def _compute_last_education_data(self):
for rec in self:
if rec.member_education_status_ids:
latest_education = rec.member_education_status_ids.sorted(
key=lambda r: (
r.education_start_date or date.min,
r.create_date or datetime.min,
r.id
),
reverse=True
)[:1]
rec.last_education_levels = latest_education.education_levels
rec.case_study = latest_education.case_study
else:
rec.last_education_levels = False
rec.case_study = 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):
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', '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:
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'
@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',
'disabilities_attachment_ids',
'relationn',
'birth_date',
'is_married',
'minor_siblings',
'member_income',
'is_married',
'member_location_conf',
'state',
'is_dead',
'is_work',
'case_study',
'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 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:
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'
)
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.age < female_benefit_age
for m in rec.benefit_id.member_ids
if m.id != rec.id
)
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 (
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 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 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 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."))
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 familys ineligibility for the associations services, and the application has been rejected."))
elif rec.relationn.relation_type == 'daughter':
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'
reasons.append(_("She is employed and not enrolled in an educational institution."))
if age_exceeded:
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 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 rec.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):
ctx = dict(self.env.context or {})
ctx.update({
'active_model': 'family.member',
'active_id': self.id,
})
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',
'context': ctx,
}
def action_resume_member(self):
ctx = dict(self.env.context or {})
# 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',
'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.is_member_workflow = False
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.is_member_workflow = False
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 _check_member_phone_validation(self):
"""Validate member phone number format and uniqueness."""
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.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),
('id', '!=', record.id)
], limit=1)
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([
'|', '|',
('phone', '=', phone),
('phone2', '=', phone),
('sms_phone', '=', phone),
], limit=1)
if exist_in_family:
raise ValidationError(
_("The phone Number already exists in Family with code %s") % exist_in_family.code)