445 lines
17 KiB
Python
445 lines
17 KiB
Python
from odoo import models, fields, api, _
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class PaProgramLevel(models.Model):
|
|
_name = 'pa.program.level'
|
|
_description = 'Program Levels Screen'
|
|
_rec_name = 'name'
|
|
_order = 'code'
|
|
|
|
code = fields.Char(
|
|
string='Level Code',
|
|
required=True,
|
|
copy=False,
|
|
readonly=True,
|
|
index=True,
|
|
default=lambda self: self.env['ir.sequence'].next_by_code('pa.program.level') or _('New'),
|
|
)
|
|
|
|
name = fields.Char(
|
|
string='Level Name',
|
|
required=True,
|
|
default=lambda self: _('Technical Tracks'),
|
|
help='Enter the level name such as: cultural, cognitive, religious, developmental, preventive, therapeutic',
|
|
)
|
|
|
|
type = fields.Selection([
|
|
('route', 'Tracks'),
|
|
('activity', 'Activities'),
|
|
('medad', 'Medad'),
|
|
], string='Level Type', required=True, help='Select the type of level')
|
|
|
|
description = fields.Text(string='Level Description')
|
|
|
|
active = fields.Boolean(string='Level Status', default=True)
|
|
|
|
track_ids = fields.One2many(
|
|
'pa.program.track',
|
|
'level_id',
|
|
string='Associated Tracks',
|
|
help='Tracks that belong to this level',
|
|
)
|
|
|
|
|
|
# Optional: enforce unique code at Python level
|
|
@api.model
|
|
def create(self, vals):
|
|
if 'code' in vals:
|
|
existing = self.search([('code', '=', vals['code'])])
|
|
if existing:
|
|
raise ValidationError(_('كود المستوى يجب أن يكون فريدًا'))
|
|
return super(PaProgramLevel, self).create(vals)
|
|
|
|
class PaProgramTrack(models.Model):
|
|
_name = 'pa.program.track'
|
|
_description = 'Program Track Screen'
|
|
_rec_name = 'name'
|
|
_order = 'code'
|
|
|
|
code = fields.Char(
|
|
string='Track Code',
|
|
required=True,
|
|
copy=False,
|
|
readonly=True,
|
|
index=True,
|
|
default=lambda self: self.env['ir.sequence'].next_by_code('pa.program.track') or _('New'),
|
|
)
|
|
name = fields.Char(string='Track Name', required=True)
|
|
description = fields.Text(string='Track Description')
|
|
|
|
# branch = fields.Many2one('age.category',string='Branch')
|
|
branch = fields.Many2one("branch.settings", string='Branch', domain="[('branch_type','=','branches')]")
|
|
|
|
|
|
gender = fields.Selection(
|
|
[('male', 'Male'), ('female', 'Female')],
|
|
string='Gender',
|
|
)
|
|
|
|
age_category = fields.Many2one(
|
|
'age.category',
|
|
string='Age Category',
|
|
help='Filter track based on age category',
|
|
)
|
|
|
|
study_category = fields.Many2one('education.level',string='Study Category')
|
|
hobby = fields.Char(string='Hobby')
|
|
study_status = fields.Char(string='Study Status')
|
|
health_status = fields.Char(string='Health Status')
|
|
education_status = fields.Char(string='Educational Status')
|
|
|
|
partner_id = fields.Many2one(
|
|
'res.partner',
|
|
string='Associated Partner',
|
|
help='The partner linked to the track'
|
|
)
|
|
|
|
active = fields.Boolean(string='Track Status', default=True)
|
|
|
|
level_id = fields.Many2one(
|
|
'pa.program.level',
|
|
string='Associated Level',
|
|
required=True,
|
|
ondelete='cascade'
|
|
)
|
|
|
|
|
|
|
|
class PaProgram(models.Model):
|
|
|
|
_name = 'pa.program'
|
|
_description = 'Programs Screen'
|
|
_rec_name = 'name'
|
|
_order = 'code'
|
|
|
|
code = fields.Char(
|
|
string='Program Code',
|
|
required=True,
|
|
copy=False,
|
|
readonly=True,
|
|
index=True,
|
|
default=lambda self: self.env['ir.sequence'].next_by_code('pa.program') or _('New'),
|
|
)
|
|
|
|
name = fields.Char(string='Program Name', required=True)
|
|
|
|
program_type_id = fields.Many2one(
|
|
'pa.program.type',
|
|
string='Program Type',
|
|
|
|
)
|
|
|
|
payment_type = fields.Selection([
|
|
('paid', 'Paid'),
|
|
('unpaid', 'Unpaid')
|
|
], string='Payment Type', required=True)
|
|
|
|
sponsor_id = fields.Many2one('takaful.sponsorship', string='Linked Sponsor',
|
|
help='Sponsor is shown only if the payment type is Paid'
|
|
)
|
|
|
|
sponsor_support_amount = fields.Float(
|
|
string='Allocated Support Amount',
|
|
related='sponsor_id.total_sponsorship_amount',
|
|
readonly=True,
|
|
help='Support amount as per sponsor details'
|
|
)
|
|
|
|
budget = fields.Float(
|
|
string='Program Budget',
|
|
help='Used when payment type = Unpaid'
|
|
)
|
|
|
|
location = fields.Selection([
|
|
('inside', 'Internal'),
|
|
('outside', 'External')
|
|
], string='Program Location', required=True)
|
|
|
|
description = fields.Text(string='Program Description')
|
|
|
|
track_id = fields.Many2one(
|
|
'pa.program.track',
|
|
string='Associated Track',
|
|
help='Each program is linked to one track that contains several activities'
|
|
)
|
|
|
|
analytic_account_id = fields.Many2one(
|
|
'account.analytic.account',
|
|
string='Linked Analytic Account',
|
|
readonly=True,
|
|
help='Automatically created analytic account with the same name as the program and cannot be deleted'
|
|
)
|
|
|
|
estimated_budget = fields.Float(
|
|
string='Associated Estimated Budget',
|
|
help='Estimated budget related to the analytic account'
|
|
)
|
|
|
|
active = fields.Boolean(string='Program Status', default=True)
|
|
|
|
@api.constrains('payment_type', 'sponsor_id')
|
|
def _check_sponsor_if_paid(self):
|
|
for record in self:
|
|
if record.payment_type == 'paid' and not record.sponsor_id:
|
|
raise ValidationError(_('يجب اختيار الكافل في حالة نوع الدفع مدفوع'))
|
|
if record.payment_type == 'unpaid' and record.sponsor_id:
|
|
raise ValidationError(_('لا يجب اختيار كافل في حالة نوع الدفع غير مدفوع'))
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
res = super().create(vals) # ✅ fix
|
|
if not res.analytic_account_id:
|
|
account = self.env['account.analytic.account'].create({
|
|
'name': res.name,
|
|
'active': True,
|
|
})
|
|
res.analytic_account_id = account.id
|
|
return res
|
|
|
|
def write(self, vals):
|
|
res = super().write(vals) # ✅ same correction here
|
|
for record in self:
|
|
if not record.analytic_account_id:
|
|
account = self.env['account.analytic.account'].create({
|
|
'name': record.name,
|
|
'active': True,
|
|
})
|
|
record.analytic_account_id = account.id
|
|
return res
|
|
|
|
class PaProgramActivity(models.Model):
|
|
_name = 'pa.program.activity'
|
|
_description = 'Program Activity'
|
|
_rec_name = 'name'
|
|
|
|
code = fields.Char("Activity Code", required=True, copy=False, default=lambda self: self.env['ir.sequence'].next_by_code('pa.program.activity'))
|
|
name = fields.Char("Activity Name", required=True)
|
|
description = fields.Text("Activity Description")
|
|
program_id = fields.Many2one('pa.program', string="Related Program")
|
|
location = fields.Selection([('inside', 'Internal'), ('outside', 'External')], required=True, string="Activity Location")
|
|
date_start = fields.Datetime("Activity Start Date")
|
|
date_end = fields.Datetime("Activity End Date")
|
|
analytic_account_id = fields.Many2one('account.analytic.account', string="Analytic Account")
|
|
estimated_budget = fields.Float("Estimated Budget")
|
|
active = fields.Boolean("Active", default=True)
|
|
|
|
|
|
class PaProgramMedad(models.Model):
|
|
_name = 'pa.program.medad'
|
|
_description = 'Medad'
|
|
_rec_name = 'name'
|
|
|
|
code = fields.Char("Medad Code", required=True, copy=False, default=lambda self: self.env['ir.sequence'].next_by_code('pa.program.medad'))
|
|
name = fields.Char("Medad Name", required=True)
|
|
description = fields.Text("Medad Description")
|
|
medad_type = fields.Selection([
|
|
('skills', 'Skills'),
|
|
('training', 'Training'),
|
|
('innovation', 'Innovation Support')
|
|
], string="Medad Type", required=True)
|
|
activity_id = fields.Many2one('pa.program.activity', string="Associated Activity")
|
|
analytic_account_id = fields.Many2one('account.analytic.account', string="Associated Analytic Account")
|
|
estimated_budget = fields.Float("Estimated Budget")
|
|
active = fields.Boolean("Active", default=True)
|
|
|
|
|
|
# Example family/beneficiary for demo: you should replace with your actual model
|
|
|
|
|
|
|
|
class PaProgramActivityRegistration(models.Model):
|
|
_name = 'pa.program.activity.registration'
|
|
_description = 'Activity Registration'
|
|
_inherit = ['mail.thread']
|
|
|
|
name = fields.Char(string='Request Number', readonly=True, index=True,
|
|
default=lambda self: self.env['ir.sequence'].next_by_code('pa.program.activity.registration') or 'New')
|
|
|
|
request_date = fields.Datetime(string='Request Date', default=fields.Datetime.now, readonly=True)
|
|
creator_id = fields.Many2one('res.users', string='Creator', default=lambda self: self.env.user, readonly=True)
|
|
|
|
date_from = fields.Datetime(string='Date From')
|
|
date_to = fields.Datetime(string='Date To')
|
|
|
|
familye_id = fields.Many2one('grant.benefit', string='Family')
|
|
beneficiary_ids = fields.Many2many(
|
|
'family.member',
|
|
string='Beneficiary Name',
|
|
domain="[('benefit_id', '=', familye_id)]"
|
|
)
|
|
benefit_category_id = fields.Many2one(related='familye_id.benefit_category_id', string='Family Category', readonly=True)
|
|
|
|
# beneficiary_ids = fields.Many2many('family.member', string='Beneficiary Name')
|
|
sms_phone = fields.Char(related='familye_id.sms_phone', string='Mobile Number', readonly=True)
|
|
|
|
member_ids = fields.One2many(
|
|
'family.member',
|
|
'benefit_ids',
|
|
string='Family Members'
|
|
)
|
|
|
|
branch_custom_id = fields.Many2one(string='Branch', related='familye_id.branch_custom_id', readonly=True)
|
|
gender = fields.Selection(related='familye_id.gender', readonly=True)
|
|
nationality_id = fields.Many2one(string='Nationality', related='familye_id.nationality_id', readonly=True)
|
|
graduation_status = fields.Selection(string='Graduation Status', related='familye_id.graduation_status', readonly=True)
|
|
health_status = fields.Selection(string='Health Status', related='familye_id.health_status', readonly=True)
|
|
education_status = fields.Selection(string='Educational Status', related='familye_id.education_status', readonly=True)
|
|
|
|
mother_is_life = fields.Boolean(string='Is Mother Alive?', related='familye_id.mother_is_life', readonly=True)
|
|
mother_location_conf = fields.Many2one(string='Mother Location', related='familye_id.mother_location_conf', readonly=True)
|
|
is_mother_work = fields.Boolean(string='Is Mother Working?', related='familye_id.is_mother_work', readonly=True)
|
|
housing_type = fields.Selection(string='Housing Type', related='familye_id.housing_type', readonly=True)
|
|
|
|
beneficiary_relation = fields.Selection([
|
|
('mother', 'Mother'),
|
|
('son', 'Son'),
|
|
('daughter', 'Daughter')
|
|
], string='Beneficiary Relation')
|
|
|
|
|
|
|
|
sons = fields.Integer(
|
|
string="Number of Sons",
|
|
compute='_compute_sons_daughters',
|
|
store=False
|
|
)
|
|
daughters = fields.Integer(
|
|
string="Number of Daughters",
|
|
compute='_compute_sons_daughters',
|
|
store=False
|
|
)
|
|
|
|
|
|
@api.depends('familye_id', 'familye_id.member_ids', 'familye_id.member_ids.relationn',
|
|
'familye_id.member_ids.relationn.relation_type')
|
|
def _compute_sons_daughters(self):
|
|
for rec in self:
|
|
sons = 0
|
|
daughters = 0
|
|
members = rec.familye_id.member_ids.filtered(lambda m: m.relationn)
|
|
for member in members:
|
|
if member.relationn.relation_type == 'son':
|
|
sons += 1
|
|
elif member.relationn.relation_type == 'daughter':
|
|
daughters += 1
|
|
rec.sons = sons
|
|
rec.daughters = daughters
|
|
|
|
level_id = fields.Many2one('pa.program.level', string='مستويات البرامج', required=True)
|
|
track_id = fields.Many2one('pa.program.track', string='مسارات البرامج')
|
|
program_id = fields.Many2one('pa.program', string='البرامج')
|
|
activity_id = fields.Many2one('pa.program.activity', string='الانشطة')
|
|
medad_id = fields.Many2one('pa.program.medad', string='مداد')
|
|
want_transport = fields.Selection([('yes', 'نعم'), ('no', 'لا')], string='هل يرغب المستفيد في مواصلات')
|
|
|
|
state = fields.Selection([
|
|
('draft', 'مسودة'),
|
|
('social_specialist', 'اخصائي اجتماعي'),
|
|
('operation_manager', 'رئيس العمليات'),
|
|
('branch_manager', 'مدير الفرع'),
|
|
('activity_head', 'رئيس الأنشطة'),
|
|
('finance_manager', 'إدارة المالية'),
|
|
('approved', 'معتمد'),
|
|
('refused', 'مرفوض')
|
|
], default='draft', tracking=True)
|
|
|
|
rejection_reason = fields.Text('سبب الارجاع') # To hold the last rejection note
|
|
|
|
@api.onchange('familye_id')
|
|
def _onchange_familye_id(self):
|
|
if self.familye_id:
|
|
# Set member_ids to the current members of the selected familye_id
|
|
# This links existing family.member records (no create)
|
|
self.member_ids = [(6, 0, self.familye_id.member_ids.ids)]
|
|
else:
|
|
self.member_ids = [(5, 0, 0)] # Clear if no familye_id
|
|
|
|
# Validation of required fields based on level type
|
|
@api.constrains('level_id', 'track_id', 'program_id', 'activity_id', 'medad_id')
|
|
def _check_required_fields_based_on_level(self):
|
|
for rec in self:
|
|
if not rec.level_id:
|
|
continue
|
|
lvl_type = rec.level_id.type
|
|
if lvl_type == 'route': # مسارات
|
|
if not rec.track_id or not rec.program_id:
|
|
raise ValidationError(_('لحالة المستوى "مسارات"، الحقول "المسار" و "البرنامج" مطلوبان'))
|
|
elif lvl_type == 'activity': # أنشطة
|
|
if not rec.track_id or not rec.program_id or not rec.activity_id:
|
|
raise ValidationError(_('لحالة المستوى "أنشطة"، الحقول "المسار" و "البرنامج" و "النشاط" مطلوبة'))
|
|
elif lvl_type == 'medad': # مداد
|
|
if not rec.track_id or not rec.program_id or not rec.activity_id or not rec.medad_id:
|
|
raise ValidationError(_('لحالة المستوى "مداد"، جميع الحقول "المسار"، "البرنامج"، "النشاط"، "المداد" مطلوبة'))
|
|
|
|
def action_to_social_specialist(self):
|
|
self.ensure_one()
|
|
self.state = 'social_specialist'
|
|
|
|
def action_approve(self):
|
|
self.ensure_one()
|
|
transition_map = {
|
|
'social_specialist': 'operation_manager',
|
|
'operation_manager': 'branch_manager',
|
|
'branch_manager': 'activity_head',
|
|
'activity_head': 'finance_manager',
|
|
'finance_manager': 'approved',
|
|
}
|
|
next_state = transition_map.get(self.state)
|
|
if next_state:
|
|
self.state = next_state
|
|
|
|
def action_refuse(self):
|
|
self.ensure_one()
|
|
self.state = 'refused'
|
|
|
|
def action_reset_to_draft(self):
|
|
self.ensure_one()
|
|
self.state = 'draft'
|
|
|
|
def action_return_to_specialist(self):
|
|
self.ensure_one()
|
|
self.state = 'social_specialist'
|
|
|
|
|
|
class PaReturnReasonWizard(models.TransientModel):
|
|
_name = 'pa.return.reason.wizard'
|
|
_description = 'Return Reason Wizard'
|
|
|
|
reason = fields.Text(string='Return Reason', required=True)
|
|
registration_id = fields.Many2one('pa.program.activity.registration', string='Registration Request')
|
|
|
|
|
|
def action_confirm_return(self):
|
|
if self.registration_id:
|
|
self.registration_id.rejection_reason = self.reason
|
|
# move back to draft or social_specialist depending on the context/state
|
|
if self.registration_id.state == 'operation_manager':
|
|
self.registration_id.state = 'social_specialist'
|
|
else:
|
|
self.registration_id.state = 'draft'
|
|
return {'type': 'ir.actions.act_window_close'}
|
|
|
|
class PaProgram(models.Model):
|
|
_name = 'pa.family'
|
|
|
|
class PaProgramType(models.Model):
|
|
_name = 'pa.program.type'
|
|
|
|
pg_type =fields.Char(string='Name')
|
|
|
|
class FamilyMember(models.Model):
|
|
_inherit = 'family.member'
|
|
|
|
benefit_ids = fields.Many2one(
|
|
'pa.program.activity.registration',
|
|
string='Benefit Registration',
|
|
ondelete='cascade',
|
|
index=True,
|
|
)
|
|
|
|
need_trans = fields.Boolean(string='Need Transportation')
|
|
need_medicin = fields.Boolean(string='Need Medicien')
|
|
need_mental_dis = fields.Boolean(string='Have Mental Disorders')
|
|
|