Merge pull request #5656 from expsa/youi

[IMP] odex_benefit: IMP benefit
This commit is contained in:
kchyounes19 2025-12-08 15:19:48 +01:00 committed by GitHub
commit 9b70cd2c7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 278 additions and 119 deletions

View File

@ -4,9 +4,9 @@
<!-- Scheduler for Managing Sponsorship Workflow Every Day -->
<record id="scheduler_visit_workflow_action" forcecreate='True' model="ir.cron">
<field name="name">Recurrence Visit Workflow Scheduler</field>
<field name="user_id" ref="base.user_root" />
<field name="user_id" ref="base.user_root"/>
<field name="interval_number">1</field>
<field name="nextcall" eval="(DateTime.now() + relativedelta(days=1)).strftime('%Y-%m-%d 00:00:00')" />
<field name="nextcall" eval="(DateTime.now() + relativedelta(days=1)).strftime('%Y-%m-%d 00:00:00')"/>
<!-- <field name="nextcall" eval="(datetime.now() + timedelta(minutes=7)).strftime('%Y-%m-%d %H:%M:%S')"/>-->
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
@ -14,16 +14,17 @@
<field name="model_id" ref="model_grant_benefit"/>
<field name="code">model.create_scheduled_visit()</field>
<field name="state">code</field>
<field name="priority" eval="5" />
<field name="priority" eval="5"/>
<field name="active" eval="True"/>
</record>
<!-- Cron for send notification for expiry date attachment for family-->
<!-- Cron for send notification for expiry date attachment for family-->
<record id="ir_cron_send_notification" model="ir.cron">
<field name="name">Notification: Expiry date Attachment</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall" eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="nextcall"
eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_grant_benefit"/>
<field name="code">model.send_expiry_date_notification()</field>
@ -34,35 +35,38 @@
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall" eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d 00:00:00')"/>
<field name="nextcall"
eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d 00:00:00')"/>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_grant_benefit"/>
<field name="code">model.update_data_automatically()</field>
<field name="state">code</field>
</record>
<record id="ir_cron_auto_exception" model="ir.cron">
</record>
<record id="ir_cron_auto_exception" model="ir.cron">
<field name="name">Check Temporarily Exception</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall" eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d 00:00:00')"/>
<field name="nextcall"
eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d 00:00:00')"/>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_grant_benefit"/>
<field name="code">model.action_auto_exception()</field>
<field name="state">code</field>
</record>
<record id="ir_cron_auto_member_exception" model="ir.cron">
</record>
<record id="ir_cron_auto_member_exception" model="ir.cron">
<field name="name">Check Member Temporarily Exception</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall" eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d 00:00:00')"/>
<field name="nextcall"
eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d 00:00:00')"/>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_family_member"/>
<field name="code">model.action_auto_exception()</field>
<field name="state">code</field>
</record>
<record id="cron_send_attachment_expiry_emails" model="ir.cron">
</record>
<record id="cron_send_attachment_expiry_emails" model="ir.cron">
<field name="name">Send Expiring Salary Attachments Notifications</field>
<field name="model_id" ref="model_salary_line"/>
<field name="state">code</field>
@ -72,5 +76,29 @@
<field name="numbercall">-1</field>
<field name="active" eval="True"/>
</record>
<record id="cron_auto_final_suspend_benefit" model="ir.cron">
<field name="name">Auto Final Suspend (Benefit)</field>
<field name="model_id" ref="model_grant_benefit"/>
<field name="state">code</field>
<field name="code">model.run_auto_final_suspend()</field>
<field name="doall" eval="False"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall"
eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
</record>
<record id="cron_auto_final_suspend_member" model="ir.cron">
<field name="name">Auto Final Suspend (Member)</field>
<field name="model_id" ref="model_family_member"/>
<field name="state">code</field>
<field name="code">model.run_auto_final_suspend()</field>
<field name="doall" eval="False"/>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="nextcall"
eval="(DateTime.today() + relativedelta(hours=0, minutes=0)).strftime('%Y-%m-%d %H:%M:%S')"/>
</record>
</data>
</odoo>

View File

@ -10966,7 +10966,6 @@ msgstr "رابط الاستبيان"
#. module: odex_benefit
#: model:ir.model.fields.selection,name:odex_benefit.selection__family_member__suspend_type__suspend
#: model:ir.model.fields.selection,name:odex_benefit.selection__grant_benefit__suspend_type__suspend
#: model:ir.model.fields.selection,name:odex_benefit.selection__suspend_reason_wizard__suspend_type__suspend
msgid "Suspend"
msgstr "إيقاف نهائي"
@ -11012,7 +11011,7 @@ msgstr "سبب الإيقاف"
#: model:ir.model,name:odex_benefit.model_suspend_reason_wizard
#, python-format
msgid "Suspend Reason Wizard"
msgstr "سبب إيقاف الخدمات"
msgstr "سبب إيقاف"
#. module: odex_benefit
#: model_terms:ir.ui.view,arch_db:odex_benefit.family_member_form
@ -11023,7 +11022,6 @@ msgstr "رفض الإيقاف"
#. module: odex_benefit
#: model:ir.model.fields,field_description:odex_benefit.field_family_member__suspend_type
#: model:ir.model.fields,field_description:odex_benefit.field_grant_benefit__suspend_type
#: model:ir.model.fields,field_description:odex_benefit.field_suspend_reason_wizard__suspend_type
#: model_terms:ir.ui.view,arch_db:odex_benefit.grant_benefit_search
msgid "Suspend Type"
msgstr "نوع الإيقاف"
@ -11092,12 +11090,11 @@ msgstr "استثناء مؤقت"
#. module: odex_benefit
#: model:ir.model.fields.selection,name:odex_benefit.selection__family_member__suspend_type__temporarily_suspend
#: model:ir.model.fields.selection,name:odex_benefit.selection__grant_benefit__suspend_type__temporarily_suspend
#: model:ir.model.fields.selection,name:odex_benefit.selection__suspend_reason_wizard__suspend_type__temporarily_suspend
#: model_terms:ir.ui.view,arch_db:odex_benefit.family_member_form
#: model_terms:ir.ui.view,arch_db:odex_benefit.grant_benefit_form
#: model_terms:ir.ui.view,arch_db:odex_benefit.grant_benefit_search
msgid "Temporarily Suspended"
msgstr "إيقاف مبدئي"
msgstr "إيقاف"
#. module: odex_benefit
#: code:addons/odex_benefit/models/benefit.py:0

View File

@ -395,9 +395,11 @@ class GrantBenefitProfile(models.Model):
('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'),
@ -618,6 +620,7 @@ class GrantBenefitProfile(models.Model):
total_father_families = fields.Integer(string="Father Families", compute='_compute_total_families')
total_mother_families = fields.Integer(string="Mother Families", compute='_compute_total_families')
total_replacement_mother_families = fields.Integer(string="Replacement Mother Families", compute='_compute_total_families')
final_suspend_date = fields.Date(string="Final Suspend Date")
_sql_constraints = [
('unique_code', "unique (code) WHERE state NOT IN ('draft', 'new')", 'This code already exists')
@ -1614,11 +1617,6 @@ class GrantBenefitProfile(models.Model):
self.action_type = 'edit_info'
def action_suspend(self):
self.is_excluded_suspension = False
for rec in self.member_ids:
rec.is_excluded_suspension = False
if rec.is_member_workflow:
rec.is_member_workflow = False
return {
'name': _('Suspend Reason Wizard'),
'view_mode': 'form',
@ -1635,8 +1633,20 @@ class GrantBenefitProfile(models.Model):
def action_suspend_second_accept(self):
for rec in self:
rec.state = 'suspended_second_approve'
rec.action_type = 'suspended'
if rec.suspend_reason.need_service_manager_approval:
rec.state = 'family_services_manager'
elif rec.suspend_reason.suspend_type == 'temporarily_suspend':
rec.state = 'temporary_suspended'
else:
rec.state = 'suspended_second_approve'
def action_suspend_third_accept(self):
for rec in self:
if rec.suspend_reason.suspend_type == 'temporarily_suspend':
rec.state = 'temporary_suspended'
else:
rec.state = 'suspended_second_approve'
def action_auto_suspend(self):
# Fetch grants in second approval state that are not excluded from suspension
@ -1715,6 +1725,16 @@ class GrantBenefitProfile(models.Model):
for member in rec.member_ids:
member.is_excluded_suspension = False
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 = 'suspended_second_approve'

View File

@ -337,7 +337,7 @@ class Salary(models.Model):
def action_refuse(self):
self.state = 'refused'
def get_salary_attachment_name(self):
"""Return salary attachment name without extension."""
if self.salary_attach:
@ -347,9 +347,9 @@ class Salary(models.Model):
def get_notification_emails(self):
"""Return a list of valid emails to notify, removing False values."""
emails = self.benefit_id.researcher_id.employee_id.mapped('work_email') or []
emails.extend([
self.benefit_id.branch_custom_id.branch.manager_id.work_email,
self.benefit_id.branch_custom_id.branch.manager_id.work_email,
self.benefit_id.branch_custom_id.branch.operation_manager_id.work_email
])
@ -357,7 +357,7 @@ class Salary(models.Model):
emails = list(filter(None, emails)) # Filters out None and False values
return ','.join(emails) if emails else 'admin@example.com'
def action_send_attachment_expiry_email(self):
"""Send email notifications for attachments expiring today and log the body in the chatter."""
today = date.today()
@ -714,6 +714,15 @@ class SuspendReason(models.Model):
is_family_return_reason = fields.Boolean(string="Family Return Reason",default=False)
is_incomplete_visit_reason = fields.Boolean(string="Incomplete Visit Reason",default=False)
active = fields.Boolean(default=True)
entity_type = fields.Selection([('family', 'Family'),('member', 'Member')],string='Entity Type')
allow_service = fields.Boolean(string="Allow Service Use", default=False)
days_before_final_suspend = fields.Integer(string="Days Before Final Suspension",
default=0,help="Number of days before the suspension becomes final.")
suspend_type = fields.Selection(
selection=[('temporarily_suspend', 'Temporarily Suspended'), ('suspend', 'Suspend')], string="Suspend Type",)
need_service_manager_approval = fields.Boolean(
string="Needs Service Manager Approval"
)
class ReturnReason(models.Model):
_name = "return.reason"

View File

@ -209,9 +209,11 @@ class FamilyMemberProfile(models.Model):
('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'),
@ -222,9 +224,11 @@ class FamilyMemberProfile(models.Model):
('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'),
@ -243,7 +247,8 @@ class FamilyMemberProfile(models.Model):
suspend_reason = fields.Many2one('suspend.reason', string='Suspend Reason')
reason = fields.Text(string='Reason')
suspend_description = fields.Text(string='Suspend Description')
suspend_attachment = fields.Binary(string='Suspend Attachment', attachment=True)
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",
@ -268,6 +273,7 @@ class FamilyMemberProfile(models.Model):
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")
# def create(self, vals):
# for line_vals in vals:
@ -810,9 +816,6 @@ class FamilyMemberProfile(models.Model):
# Member Suspend Manual
def action_suspend(self):
for rec in self:
rec.is_member_workflow = True
rec.is_excluded_suspension = False
return {
'name': _('Suspend Reason Wizard'),
'view_mode': 'form',
@ -874,6 +877,16 @@ class FamilyMemberProfile(models.Model):
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'

View File

@ -943,10 +943,19 @@
<group>
<group>
<field name="is_stop_reason" widget="boolean_toggle"/>
<field name="is_reject_reason" widget="boolean_toggle"/>
<field name="is_return_reason" widget="boolean_toggle"/>
<field name="need_service_manager_approval" widget="boolean_toggle"
attrs="{'invisible': [('is_stop_reason','=',False)]}"/>
<field name="entity_type"
attrs="{'invisible': [('is_stop_reason','=',False)]}"/>
<field name="suspend_type"
attrs="{'invisible': [('is_stop_reason','=',False)]}"/>
<field name="days_before_final_suspend"
attrs="{'invisible': [('suspend_type','!=','temporarily_suspend')]}"/>
<field name="allow_service" widget="boolean_toggle"/>
</group>
<group>
<field name="is_reject_reason" widget="boolean_toggle"/>
<field name="is_return_reason" widget="boolean_toggle"/>
<field name="is_family_return_reason" widget="boolean_toggle"/>
<field name="is_incomplete_visit_reason" widget="boolean_toggle"/>
</group>
@ -966,6 +975,7 @@
<field name="is_return_reason" widget="boolean_toggle"/>
<field name="is_family_return_reason" widget="boolean_toggle"/>
<field name="is_incomplete_visit_reason" widget="boolean_toggle"/>
<field name="allow_service" widget="boolean_toggle"/>
</tree>
</field>
</record>
@ -1008,7 +1018,8 @@
<group>
<field name="name"/>
<field name="relation_type"/>
<field name="age_difference" attrs="{'invisible': [('relation_type', 'not in', ['son', 'daughter'])]}"/>
<field name="age_difference"
attrs="{'invisible': [('relation_type', 'not in', ['son', 'daughter'])]}"/>
</group>
</group>
</sheet>

View File

@ -66,11 +66,11 @@
<field name="create_date"/>
<field name="district_id"/>
<field name="mobile"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click #{record.benefit_category_id.value == 'غير مستفيدة' ? 'inactive_family_card' : ''}"
t-attf-style="background: linear-gradient(135deg, #{record.benefit_category_id.value == 'غير مستفيدة' ? '#fff5f5' : '#ffffff'} 0%, #{record.benefit_category_id.value == 'غير مستفيدة' ? '#ffe0e0' : '#f8f9fa'} 100%); border: 1px solid #{record.benefit_category_id.value == 'غير مستفيدة' ? '#dc3545' : '#dee2e6'}; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); transition: all 0.3s ease; margin-bottom: 15px; overflow: hidden; position: relative; min-height: 380px; opacity: #{record.benefit_category_id.value == 'غير مستفيدة' ? '0.85' : '1'};">
<div t-attf-class="oe_kanban_global_click #{record.benefit_category_id.value == 'غير مستفيدة' ? 'inactive_family_card' : ''}"
t-attf-style="background: linear-gradient(135deg, #{record.benefit_category_id.value == 'غير مستفيدة' ? '#fff5f5' : '#ffffff'} 0%, #{record.benefit_category_id.value == 'غير مستفيدة' ? '#ffe0e0' : '#f8f9fa'} 100%); border: 1px solid #{record.benefit_category_id.value == 'غير مستفيدة' ? '#dc3545' : '#dee2e6'}; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); transition: all 0.3s ease; margin-bottom: 15px; overflow: hidden; position: relative; min-height: 380px; opacity: #{record.benefit_category_id.value == 'غير مستفيدة' ? '0.85' : '1'};">
<div style="background: linear-gradient(135deg, #05887e 0%, #036d64 100%); color: white; padding: 8px 10px 90px 10px; font-weight: 700; position: relative;">
<div style="position: absolute; top: 8px; right: 10px; background: rgba(255,255,255,0.95); color: #198754; padding: 6px 10px; border-radius: 6px; font-size: 13px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1); z-index: 1;">
<field name="code"/>
@ -78,17 +78,17 @@
<t t-if="record.benefit_category_id.raw_value">
<div style="position: absolute; top: 8px; left: 10px; background: rgba(255,255,255,0.95); padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1); z-index: 1;">
<i t-attf-class="fa #{record.benefit_category_id.value == 'غير مستفيدة' ? 'fa-times-circle' : 'fa-check-circle'}"
t-attf-style="color: #{record.benefit_category_id.value == 'غير مستفيدة' ? '#dc3545' : '#198754'}; margin-left: 4px; font-size: 13px;"/>
<i t-attf-class="fa #{record.benefit_category_id.value == 'غير مستفيدة' ? 'fa-times-circle' : 'fa-check-circle'}"
t-attf-style="color: #{record.benefit_category_id.value == 'غير مستفيدة' ? '#dc3545' : '#198754'}; margin-left: 4px; font-size: 13px;"/>
<span t-attf-style="color: #{record.benefit_category_id.value == 'غير مستفيدة' ? '#dc3545' : '#198754'};">
<field name="benefit_category_id"/>
</span>
</div>
</t>
<div style="position: absolute; top: 43px; right: 10px; left: 10px; display: flex; justify-content: space-between; align-items: center; z-index: 1;">
<div style="font-size: 10px; opacity: 0.85; text-align: right;">
<span style="display: inline-block;">الفرع: </span>
<span style="display: inline-block;">الفرع:</span>
<span style="margin-right: 4px;">
<t t-if="record.branch_custom_id.raw_value">
<field name="branch_custom_id"/>
@ -98,47 +98,53 @@
</t>
</span>
</div>
<div>
<field name="state" widget="badge"
decoration-success="state in ['first_approve', 'second_approve']"
decoration-muted="state in ['draft','new']"
decoration-danger="state in ['refused','suspended_second_approve','black_list']"
decoration-warning="state in ['first_refusal','waiting_approve']"
decoration-info="state in ['complete_info']"/>
<field name="state" widget="badge"
decoration-success="state in ['first_approve', 'second_approve']"
decoration-muted="state in ['draft','new']"
decoration-danger="state in ['refused','suspended_second_approve','black_list']"
decoration-warning="state in ['first_refusal','waiting_approve']"
decoration-info="state in ['complete_info']"/>
</div>
</div>
<div style="position: absolute; top: 68px; right: 10px; left: 10px; display: flex; justify-content: space-between; align-items: center; z-index: 1;">
<div style="font-size: 10px; opacity: 0.85; text-align: right;">
<span style="display: inline-block;">تاريخ التسجيل: </span>
<span style="margin-right: 4px;"><field name="create_date" widget="date"/></span>
<span style="display: inline-block;">تاريخ التسجيل:</span>
<span style="margin-right: 4px;">
<field name="create_date" widget="date"/>
</span>
</div>
<div>
<field name="action_type" widget="badge"
decoration-muted="action_type in ['new']"
decoration-info="action_type in ['edit_info']"
decoration-success="action_type in ['approved']"
decoration-danger="action_type in ['suspended']"
decoration-warning="action_type in ['exception']"/>
<field name="action_type" widget="badge"
decoration-muted="action_type in ['new']"
decoration-info="action_type in ['edit_info']"
decoration-success="action_type in ['approved']"
decoration-danger="action_type in ['suspended']"
decoration-warning="action_type in ['exception']"/>
</div>
</div>
</div>
<div style="padding: 10px; background: white;">
<div style="margin-bottom: 14px;">
<div style="display: flex; margin-bottom: 8px;">
<div style="flex: 1; margin-left: 8px;">
<div style="display: flex; align-items: center; margin-bottom: 4px;">
<i class="fa fa-male" style="color: #198754; margin-left: 4px; font-size: 14px;"/>
<span style="color: #495057; font-size: 12px; font-weight: 700;">الأب: </span>
<i class="fa fa-male"
style="color: #198754; margin-left: 4px; font-size: 14px;"/>
<span style="color: #495057; font-size: 12px; font-weight: 700;">
الأب:
</span>
</div>
<div style="font-weight: 600; font-size: 13px; color: #212529; line-height: 1.2; max-height: 30px; overflow: hidden;">
<t t-if="record.father_name.raw_value">
<t t-if="record.father_name.raw_value.length > 15">
<span t-esc="record.father_name.raw_value.substring(0, 15) + '...'" style="display: block;"/>
<span t-esc="record.father_name.raw_value.substring(0, 15) + '...'"
style="display: block;"/>
</t>
<t t-else="">
<field name="father_name"/>
@ -156,13 +162,17 @@
</div>
<div style="flex: 1;">
<div style="display: flex; align-items: center; margin-bottom: 4px;">
<i class="fa fa-female" style="color: #e91e63; margin-left: 4px; font-size: 14px;"/>
<span style="color: #495057; font-size: 12px; font-weight: 700;">الأم: </span>
<i class="fa fa-female"
style="color: #e91e63; margin-left: 4px; font-size: 14px;"/>
<span style="color: #495057; font-size: 12px; font-weight: 700;">
الأم:
</span>
</div>
<div style="font-weight: 600; font-size: 13px; color: #212529; line-height: 1.2; max-height: 30px; overflow: hidden;">
<t t-if="record.mother_name.raw_value">
<t t-if="record.mother_name.raw_value.length > 15">
<span t-esc="record.mother_name.raw_value.substring(0, 15) + '...'" style="display: block;"/>
<span t-esc="record.mother_name.raw_value.substring(0, 15) + '...'"
style="display: block;"/>
</t>
<t t-else="">
<field name="mother_name"/>
@ -182,24 +192,36 @@
</div>
<div style="display: flex; margin-bottom: 10px; gap: 8px;">
<a name="action_view_benefit_members" type="object" style="flex: 1; text-align: center; padding: 5px 6px; background: linear-gradient(135deg, #f1f8e9 0%, #dcedc8 100%); border-radius: 8px; border: 2px solid #689f38; display: block; text-decoration: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(104,159,56,0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(245,124,0,0.3)'; this.style.background='linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(104,159,56,0.2)'; this.style.background='linear-gradient(135deg, #f1f8e9 0%, #dcedc8 100%)'">
<i class="fa fa-users" style="color: #198754; font-size: 16px; margin-bottom: 6px; display: block;"/>
<a name="action_view_benefit_members" type="object"
style="flex: 1; text-align: center; padding: 5px 6px; background: linear-gradient(135deg, #f1f8e9 0%, #dcedc8 100%); border-radius: 8px; border: 2px solid #689f38; display: block; text-decoration: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(104,159,56,0.2);"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(245,124,0,0.3)'; this.style.background='linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%)'"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(104,159,56,0.2)'; this.style.background='linear-gradient(135deg, #f1f8e9 0%, #dcedc8 100%)'">
<i class="fa fa-users"
style="color: #198754; font-size: 16px; margin-bottom: 6px; display: block;"/>
<div style="font-size: 11px; color: #198754; font-weight: 700; text-transform: uppercase; margin-bottom: 4px; line-height: 1.1;">
<div>أفراد</div>
<div>مستفيدة</div>
</div>
<div style="font-weight: 800; font-size: 14px; color: #198754;">
<t t-if="(record.benefit_member_count.raw_value || 0) > 0 or (record.non_member_count.raw_value || 0) > 0">
<span style="color: #198754; font-weight: 900; text-decoration: underline;"><t t-esc="(record.benefit_member_count.raw_value || 0)"/></span> من <t t-esc="(record.benefit_member_count.raw_value || 0) + (record.non_member_count.raw_value || 0)"/>
<span style="color: #198754; font-weight: 900; text-decoration: underline;">
<t t-esc="(record.benefit_member_count.raw_value || 0)"/>
</span>
من
<t t-esc="(record.benefit_member_count.raw_value || 0) + (record.non_member_count.raw_value || 0)"/>
</t>
<t t-else="">
--
</t>
</div>
</a>
<a name="action_open_visit_location" type="object" style="flex: 1; text-align: center; padding: 5px 6px; background: linear-gradient(135deg, #eceff1 0%, #cfd8dc 100%); border-radius: 8px; border: 2px solid #455A64; display: block; text-decoration: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(69,90,100,0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(69,90,100,0.3)'; this.style.background='linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(69,90,100,0.2)'; this.style.background='linear-gradient(135deg, #eceff1 0%, #cfd8dc 100%)'">
<i class="fa fa-home" style="color: #455A64; font-size: 16px; margin-bottom: 6px; display: block;"/>
<a name="action_open_visit_location" type="object"
style="flex: 1; text-align: center; padding: 5px 6px; background: linear-gradient(135deg, #eceff1 0%, #cfd8dc 100%); border-radius: 8px; border: 2px solid #455A64; display: block; text-decoration: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(69,90,100,0.2);"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(69,90,100,0.3)'; this.style.background='linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%)'"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(69,90,100,0.2)'; this.style.background='linear-gradient(135deg, #eceff1 0%, #cfd8dc 100%)'">
<i class="fa fa-home"
style="color: #455A64; font-size: 16px; margin-bottom: 6px; display: block;"/>
<div style="font-size: 11px; color: #455A64; font-weight: 700; text-transform: uppercase; margin-bottom: 4px; line-height: 1.1;">
<div>عدد</div>
<div>الزيارات</div>
@ -213,9 +235,13 @@
</t>
</div>
</a>
<a name="action_open_related_service_requests" type="object" style="flex: 1; text-align: center; padding: 5px 6px; background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%); border-radius: 8px; border: 2px solid #6b7280; display: block; text-decoration: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(245,124,0,0.2);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(245,124,0,0.3)'; this.style.background='linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(245,124,0,0.2)'; this.style.background='linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%)'">
<i class="fa fa-file-text" style="color: #f57c00; font-size: 16px; margin-bottom: 6px; display: block;"/>
<a name="action_open_related_service_requests" type="object"
style="flex: 1; text-align: center; padding: 5px 6px; background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%); border-radius: 8px; border: 2px solid #6b7280; display: block; text-decoration: none; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 2px 4px rgba(245,124,0,0.2);"
onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(245,124,0,0.3)'; this.style.background='linear-gradient(135deg, #fff8e1 0%, #ffecb3 100%)'"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(245,124,0,0.2)'; this.style.background='linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%)'">
<i class="fa fa-file-text"
style="color: #f57c00; font-size: 16px; margin-bottom: 6px; display: block;"/>
<div style="font-size: 11px; color: #f57c00; font-weight: 700; text-transform: uppercase; margin-bottom: 4px; line-height: 1.1;">
<div>طلبات</div>
<div>الخدمة</div>
@ -234,8 +260,9 @@
<div style="margin-bottom: 4px;">
<div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
<div style="display: flex; align-items: center; font-size: 12px;">
<i class="fa fa-map-marker" style="color: #dc3545; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">المدينة: </span>
<i class="fa fa-map-marker"
style="color: #dc3545; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">المدينة:</span>
<span style="color: #212529; margin-right: 6px; font-weight: 500;">
<t t-if="record.city_id.raw_value">
<field name="city_id"/>
@ -246,8 +273,9 @@
</span>
</div>
<div style="display: flex; align-items: center; font-size: 12px;">
<i class="fa fa-location-arrow" style="color: #6f42c1; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">الحي: </span>
<i class="fa fa-location-arrow"
style="color: #6f42c1; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">الحي:</span>
<span style="color: #212529; margin-right: 6px; font-weight: 500;">
<t t-if="record.district_id.raw_value">
<field name="district_id"/>
@ -258,14 +286,16 @@
</span>
</div>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 6px;">
<div style="display: flex; align-items: center; font-size: 12px;">
<i class="fa fa-phone" style="color: #17a2b8; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">جوال: </span>
<i class="fa fa-phone"
style="color: #17a2b8; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">جوال:</span>
<span style="color: #212529; margin-right: 6px; font-weight: 500;">
<t t-if="record.mobile.raw_value and record.mobile.raw_value.substr(0,3) === '966'">
0<t t-esc="record.mobile.raw_value.substr(3)"/>
0
<t t-esc="record.mobile.raw_value.substr(3)"/>
</t>
<t t-elif="record.mobile.raw_value">
<field name="mobile"/>
@ -276,11 +306,13 @@
</span>
</div>
<div style="display: flex; align-items: center; font-size: 12px;">
<i class="fa fa-money" style="color: #388e3c; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">الدخل: </span>
<i class="fa fa-money"
style="color: #388e3c; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">الدخل:</span>
<span style="color: #212529; margin-right: 6px; font-weight: 500;">
<t t-if="record.member_income.raw_value and record.member_income.raw_value > 0">
<field name="member_income" widget="monetary" options="{'currency_field': 'currency_id', 'field_digits': [16,0]}"/>
<field name="member_income" widget="monetary"
options="{'currency_field': 'currency_id', 'field_digits': [16,0]}"/>
</t>
<t t-else="">
<span style="color: #6c757d;">--</span>
@ -288,10 +320,11 @@
</span>
</div>
</div>
<div style="display: flex; align-items: center; font-size: 12px;">
<i class="fa fa-user-circle" style="color: #198754; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">الأخصائى: </span>
<i class="fa fa-user-circle"
style="color: #198754; margin-left: 4px; font-size: 12px;"/>
<span style="font-weight: 600; color: #495057;">الأخصائى:</span>
<span style="color: #212529; margin-right: 6px; font-weight: 500;">
<t t-if="record.researcher_id.raw_value">
<field name="researcher_id"/>
@ -306,8 +339,9 @@
<div style="background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); padding: 10px 15px; border-top: 2px solid #198754; text-align: right;">
<div style="display: inline-flex; align-items: center; font-size: 11px;">
<i class="fa fa-calendar" style="color: #6c757d; margin-left: 4px;"/>
<span style="font-weight: 600; color: #495057; margin-left: 4px;">آخر زيارة: </span>
<i class="fa fa-calendar" style="color: #6c757d; margin-left: 4px;"/>
<span style="font-weight: 600; color: #495057; margin-left: 4px;">آخر زيارة:
</span>
<span style="color: #212529; font-weight: 500;">
<t t-if="record.last_visit_date.raw_value">
<field name="last_visit_date"/>
@ -374,7 +408,15 @@
confirm="Are you sure you want to move to black list ?"/>
<button name="action_suspend" type="object"
string="Temporarily Suspended" class="btn btn-danger"
states="second_approve"
attrs="{
'invisible': [
'|',
('state', 'not in', ('second_approve', 'complete_info')),
'&amp;',
('state', '=', 'complete_info'),
('action_type', '!=', 'edit_info')
]
}"
confirm="Are you sure you want to move to Temporarily Suspended ?"/>
<button name="action_suspend_first_accept" type="object"
string="First Approve" class="oe_highlight"
@ -385,9 +427,13 @@
string="Second Approve" class="oe_highlight"
groups="odex_benefit.group_benefit_branch_manager,odex_benefit.group_benefit_manager"
attrs="{'invisible': ['|',('action_type','!=','suspended'),('state', 'not in', ['first_approve'])]}"/>
<button name="action_suspend_third_accept" type="object"
string="Third Approve" class="oe_highlight"
groups="odex_benefit.group_benefit_manager"
attrs="{'invisible': ['|',('action_type','!=','suspended'),('state', 'not in', ['family_services_manager'])]}"/>
<button name="action_suspend_refuse" type="object"
string="Suspend Refuse" class="oe_highlight"
attrs="{'invisible': ['|',('action_type','!=','suspended'),('state', 'not in', ['waiting_approve','first_approve','suspended_second_approve'])]}"
attrs="{'invisible': ['|',('action_type','!=','suspended'),('state', 'not in', ['waiting_approve','first_approve','family_services_manager','suspended_second_approve'])]}"
/>
<!-- Exception -->
<button name="action_exception" type="object"
@ -563,7 +609,7 @@
<field name="contact_type"/>
<field name="request_producer_relation" readonly="1" force_save="1"/>
<field name="applicant_name"/>
<field name="partner_id" string="Partner" readonly="1" />
<field name="partner_id" string="Partner" readonly="1"/>
<field name="request_producer" readonly="1" force_save="1" invisible="1"/>
</group>
</group>
@ -849,7 +895,8 @@
name="action_open_related_replacement_mother_families"
type="object"
attrs="{'invisible': [('total_replacement_mother_families', '=', 0)]}">
<field name="total_replacement_mother_families" string="Replacement Mother Families"
<field name="total_replacement_mother_families"
string="Replacement Mother Families"
widget="statinfo"/>
</button>
</group>
@ -1654,6 +1701,7 @@
<field name="suspend_reason"
options="{'no_create': True, 'no_create_edit': True,'no_quick_create': True, 'no_open': True}"/>
<field name="suspend_description"/>
<field name="final_suspend_date" readonly="1"/>
</group>
<group>
<field name="suspend_attachment" widget="many2many_attachment_preview"/>

View File

@ -233,9 +233,10 @@
<group>
<field name="suspend_reason"/>
<field name="suspend_description"/>
<field name="final_suspend_date" readonly="1"/>
</group>
<group>
<field name="suspend_attachment"/>
<field name="suspend_attachment" widget="many2many_attachment_preview"/>
<field name="suspend_type"/>
<field name="suspend_method" readonly="1" force_save="1"/>
</group>

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, _
from odoo import models, fields, _, api
from odoo.exceptions import UserError
from datetime import datetime, timedelta
class SuspendReasonWizard(models.TransientModel):
_name = 'suspend.reason.wizard'
@ -22,12 +22,28 @@ class SuspendReasonWizard(models.TransientModel):
entity_id = fields.Many2one("grant.benefit", string="Entity", default=_default_entity)
member_id = fields.Many2one("family.member", string="Member", default=_default_member)
suspend_reason = fields.Many2one('suspend.reason',string='Suspend Reason', required=True)
suspend_reason = fields.Many2one('suspend.reason', string='Suspend Reason',
domain=lambda self: self._get_suspend_reason_domain(), required=True)
suspend_description = fields.Text(string='Suspend Description', required=True)
suspend_attachment = fields.Binary(string='Suspend Attachment', attachment=True, required=True)
suspend_type = fields.Selection(selection=[('temporarily_suspend', 'Temporarily Suspended'), ('suspend', 'Suspend')], string="Suspend Type", required=True)
final_suspend_date = fields.Date(string="Final Suspend Date",default=fields.Date.context_today)
@api.onchange('suspend_reason')
def _onchange_compute_final_suspend_date(self):
if self.suspend_reason and self.suspend_reason.suspend_type == 'temporarily_suspend':
days = self.suspend_reason.days_before_final_suspend or 0
self.final_suspend_date = datetime.today().date() + timedelta(days=days)
else:
self.final_suspend_date = fields.Date.context_today(self)
def _get_suspend_reason_domain(self):
domain = [('is_stop_reason', '=', True)]
if self._context.get('active_model') == 'family.member':
domain.append(('entity_type', '=', 'member'))
elif self._context.get('active_model') == 'grant.benefit':
domain.append(('entity_type', '=', 'family'))
return domain
def _create_attachment_record_from_binary(self):
self.ensure_one()
@ -46,23 +62,39 @@ class SuspendReasonWizard(models.TransientModel):
}
return self.env['ir.attachment'].create(attachment_vals)
def action_submit(self):
for rec in self:
rec.entity_id.state = 'waiting_approve'
rec.entity_id.action_type = 'suspended'
rec.entity_id.suspend_reason = rec.suspend_reason
rec.entity_id.suspend_description = rec.suspend_description
rec.entity_id.suspend_type = rec.suspend_type
rec.entity_id.suspend_attachment = self._create_attachment_record_from_binary()
rec.entity_id.suspend_method = 'manual'
rec.entity_id.member_ids.write({
'is_excluded_suspension': False,
'is_member_workflow': False,
'final_suspend_date': rec.final_suspend_date,
})
attachment = rec._create_attachment_record_from_binary()
rec.entity_id.suspend_attachment = [(4, attachment.id)]
rec.entity_id.write({
'state': 'waiting_approve',
'action_type': 'suspended',
'suspend_reason': rec.suspend_reason.id,
'suspend_description': rec.suspend_description,
'suspend_type': rec.suspend_reason.suspend_type,
'suspend_method': 'manual',
'final_suspend_date': rec.final_suspend_date,
'is_excluded_suspension': False,
})
def action_member_submit(self):
for rec in self:
rec.member_id.state_a = 'waiting_approve'
rec.member_id.action_type = 'suspended'
rec.member_id.suspend_reason = rec.suspend_reason
rec.member_id.suspend_description = rec.suspend_description
rec.member_id.suspend_type = rec.suspend_type
rec.member_id.suspend_attachment = self._create_attachment_record_from_binary()
rec.member_id.suspend_method = 'manual'
rec.member_id.write({
'state_a': 'waiting_approve',
'action_type': 'suspended',
'suspend_reason': rec.suspend_reason.id,
'suspend_description': rec.suspend_description,
'suspend_type': rec.suspend_reason.suspend_type,
'suspend_method': 'manual',
'final_suspend_date': rec.final_suspend_date,
'is_member_workflow': True,
'is_excluded_suspension': False,
})
attachment = rec._create_attachment_record_from_binary()
rec.member_id.suspend_attachment = [(4, attachment.id)]

View File

@ -13,8 +13,8 @@
<field name="suspend_description"/>
</group>
<group>
<field name="suspend_type"/>
<field name="suspend_attachment"/>
<field name="final_suspend_date" readonly="1" force_save="1"/>
</group>
</group>
<footer>
@ -39,8 +39,8 @@
<field name="suspend_description"/>
</group>
<group>
<field name="suspend_type"/>
<field name="suspend_attachment"/>
<field name="final_suspend_date" readonly="1" force_save="1"/>
</group>
</group>
<footer>