Merge pull request #5388 from expsa/tt_dev_odex25_ensan

[IMP] odex_benefit: IMP benefit
This commit is contained in:
kchyounes19 2025-11-12 14:36:15 +01:00 committed by GitHub
commit dc47e69e5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 169 additions and 158 deletions

View File

@ -4206,7 +4206,6 @@ msgstr ""
#: model:ir.model.fields,field_description:odex_benefit.field_receive_food_basket__description
#: model:ir.model.fields,field_description:odex_benefit.field_rooms_categories__description
#: model:ir.model.fields,field_description:odex_benefit.field_rooms_items__description
#: model:ir.model.fields,field_description:odex_benefit.field_service_request__description
#: model:ir.model.fields,field_description:odex_benefit.field_specialization_specialization__description
#: model:ir.model.fields,field_description:odex_benefit.field_sport_type__description
#: model_terms:ir.ui.view,arch_db:odex_benefit.view_confirm_benefit_expense_form
@ -4214,6 +4213,11 @@ msgstr ""
msgid "Description"
msgstr "الوصف"
#. module: odex_benefit
#: model:ir.model.fields,field_description:odex_benefit.field_service_request__description
msgid "Description"
msgstr "ملاحظات إضافية"
#. module: odex_benefit
#: model:ir.model.fields,field_description:odex_benefit.field_visit_location__description
msgid "Description"
@ -11582,15 +11586,14 @@ msgstr "إجمالي مصروف الغذاء للأسر"
#. module: odex_benefit
#: model:ir.model.fields,field_description:odex_benefit.field_confirm_benefit_expense__total_move_lines
#: model:ir.model.fields,field_description:odex_benefit.field_confirm_benefit_expense__total_moves
#: model:ir.model.fields,field_description:odex_benefit.field_grant_benefit__total_move_lines
msgid "Total Move Lines"
msgstr "القيود اليومية"
#. module: odex_benefit
#: model:ir.model.fields,field_description:odex_benefit.field_confirm_benefit_expense__total_invoices
msgid "Total Moves"
msgstr "الحركات"
#: model:ir.model.fields,field_description:odex_benefit.field_confirm_benefit_expense__total_moves
msgid "Total Move"
msgstr "القيود اليومية"
#. module: odex_benefit
#: model:ir.model.fields,field_description:odex_benefit.field_benefit_housing__total_net
@ -14875,12 +14878,6 @@ msgstr "اسم الأب"
msgid "Account Number is required."
msgstr "رقم آيبان مطلوب."
#. module: odex_benefit
#: code:addons/odex_benefit/models/benefit.py:0
#, python-format
msgid "IBAN Document is required."
msgstr "مرفق الآيبان مطلوب."
#. module: odex_benefit
#: code:addons/odex_benefit/models/benefit.py:0
#, python-format

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import logging
from datetime import datetime, date
from email.policy import default
from dateutil.relativedelta import relativedelta as rd
from odoo import models, fields, api, _
from odoo.exceptions import UserError, ValidationError
@ -104,7 +106,7 @@ class GrantBenefitProfile(models.Model):
password = fields.Char('Password')
# Category And Family
benefit_category_id = fields.Many2one('benefit.category', string='Benefit Category', compute="get_benefit_category",store=True)
benefit_category_old = fields.Many2one('benefit.category', string='Benefit Category')
benefit_category_old = fields.Many2one('benefit.category', string='Family category in the old system')
family_id = fields.Many2one('benefit.family', string='Benefit Family')
# address
housing_id = fields.Many2one('benefit.housing', string='Benefit Housing')
@ -587,7 +589,7 @@ class GrantBenefitProfile(models.Model):
replacement_mother_disabilities_attachment_ids = fields.One2many('ir.attachment', 'replacement_mother_grant_benefit_id',string='Disabilities Attachments',domain=[('disabilities_id', '!=', False)])
sponsorship_id = fields.Many2one('takaful.sponsorship',string='Sponsorship')
#check if Account Holder is Family memeber of not
is_family_member = fields.Boolean(string='Is Family member?' , help="check if Account Holder is Family memeber")
is_family_member = fields.Boolean(string='Is Family member?' , help="check if Account Holder is Family memeber",default=True)
family_member_id = fields.Many2one('family.member', string='Account Holder Name', domain="[('id','in',member_ids)]")
owner_identity_attachment_ids = fields.Many2many('ir.attachment', 'rel_identity_attachment','benefit_id','attachment_id',string='Account holder identity')
family_approval_attachment_ids = fields.Many2many('ir.attachment', 'rel_approval_attachment','benefit_id','attachment_id',string='Family approval of transfer')
@ -1450,20 +1452,18 @@ class GrantBenefitProfile(models.Model):
"""Accept registration"""
for rec in self:
messages = []
if rec.total_family_expenses:
if not rec.acc_number:
messages.append(_("Account Number is required."))
if not rec.bank_id:
messages.append(_("Bank is required."))
if not rec.is_family_member and not rec.acc_holder_name:
messages.append(_("Account Holder Name is required for non-family members."))
if rec.is_family_member and not rec.family_member_id:
messages.append(_("Family Member must be selected."))
if not rec.acc_number:
messages.append(_("Account Number is required."))
if not rec.iban_document:
messages.append(_("IBAN Document is required."))
if not rec.bank_id:
messages.append(_("Bank is required."))
if not rec.is_family_member and not rec.acc_holder_name:
messages.append(_("Account Holder Name is required for non-family members."))
if rec.is_family_member and not rec.family_member_id:
messages.append(_("Family Member must be selected."))
if messages:
raise UserError("\n".join(messages))
if messages:
raise UserError("\n".join(messages))
if not rec.user_id:
self.sudo().create_user()

View File

@ -56,22 +56,9 @@ class ConfirmBenefitExpense(models.Model):
cloth_expense = fields.Boolean(string='Include Clothing Expense', states={'confirm': [('readonly', True)]})
payment_order_id = fields.Many2one('payment.orders', string='Payment Order', ondelete="set null", copy=False)
move_id = fields.Many2one('account.move', ondelete='cascade')
# payment_method_id = fields.Many2one(comodel_name='account.payment.method.line', string='Payment Method',
# readonly=False, store=True, copy=False,
# states={'confirm': [('readonly', True)]},
# compute='_compute_payment_method_line_id',
# domain="[('id', 'in', available_payment_method_line_ids)]",
# help="Manual: Pay or Get paid by any method outside of Odoo.\n"
# "Payment Providers: Each payment provider has its own Payment Method. Request a transaction on/to a card thanks to a payment token saved by the partner when buying or subscribing online.\n"
# "Check: Pay bills by check and print it from Odoo.\n"
# "Batch Deposit: Collect several customer checks at once generating and submitting a batch deposit to your bank. Module account_batch_payment is necessary.\n"
# "SEPA Credit Transfer: Pay in the SEPA zone by submitting a SEPA Credit Transfer file to your bank. Module account_sepa is necessary.\n"
# "SEPA Direct Debit: Get paid in the SEPA zone thanks to a mandate your partner will have granted to you. Module account_sepa is necessary.\n")
available_payment_method_line_ids = fields.Many2many(comodel_name='account.payment.method.line')
total_moves = fields.Integer(string="Total Move Lines", compute='_get_total_move_lines')
total_moves = fields.Integer(string="Total Move", compute='_get_total_move_lines')
total_move_lines = fields.Integer(string="Total Move Lines", compute='_get_total_move_lines')
total_invoices = fields.Integer(string="Total Moves", compute='_get_total_move_lines')
family_monthly_income = fields.Float(string="Total Monthly Income", compute='_get_family_monthly_values')
family_monthly_meals = fields.Float(string="Total Monthly Meals", compute='_get_family_monthly_values')
family_monthly_clotting = fields.Float(string="Total Monthly Clotting", compute='_get_family_monthly_values')
@ -208,6 +195,7 @@ class ConfirmBenefitExpense(models.Model):
# Search for conflicting records of the same expense type within the past month
conflicting_records = self.search([
('id', '!=', rec._origin.id),
('start_date', '<=', rec.end_date),
('end_date', '>=', rec.start_date),
('expense_type', '=', rec.expense_type),
@ -220,6 +208,11 @@ class ConfirmBenefitExpense(models.Model):
rec.family_domain_ids = self.env['grant.benefit'].search(base_domain)
@api.onchange('branch_custom_id')
def _onchange_branch_custom_id(self):
if self.branch_custom_id:
self.family_ids = [(5, 0, 0)]
def unlink(self):
for rec in self:
if rec.state not in ['draft']:
@ -270,74 +263,29 @@ class ConfirmBenefitExpense(models.Model):
def _get_total_move_lines(self):
for rec in self:
rec.total_moves = self.env['account.move'].search_count([
('family_confirm_id', '=', rec.id), ('move_type', '!=', 'in_invoice')
])
rec.total_invoices = self.env['account.move'].search_count([
('family_confirm_id', '=', rec.id), ('move_type', '=', 'in_invoice')
])
rec.total_move_lines = len(self.env['account.move'].search([
('family_confirm_id', '=', rec.id), ('move_type', '!=', 'in_invoice')
]).mapped('line_ids').ids)
if self.expense_type == 'family_expense':
moves = self.payment_order_id.move_id
else:
moves = self.move_id
rec.total_moves = len(moves)
rec.total_move_lines = len(moves.mapped('line_ids'))
def action_open_related_move_records(self):
""" Opens a tree view with related records filtered by a dynamic domain """
moves = self.env['account.move'].search([
('family_confirm_id', '=', self.id), ('move_type', '!=', 'in_invoice')
]).ids
return {
'name': _('Moves'),
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'view_mode': 'tree,form',
'domain': [('id', 'in', moves)],
}
def action_open_related_move_line_records(self):
""" Opens a tree view with related records filtered by a dynamic domain """
move_lines = self.env['account.move'].search([
('family_confirm_id', '=', self.id), ('move_type', '!=', 'in_invoice')
]).mapped('line_ids').ids
return {
'name': _('Moves'),
'type': 'ir.actions.act_window',
'res_model': 'account.move.line',
'view_mode': 'tree,form',
'domain': [('id', 'in', move_lines)],
}
def action_open_related_invoice_records(self):
""" Opens a tree view with related records filtered by a dynamic domain """
invoices = self.env['account.move'].search([
('family_confirm_id', '=', self.id), ('move_type', '=', 'in_invoice')
]).ids
if self.expense_type == 'family_expense':
moves = self.payment_order_id.move_id.ids
else:
moves = self.move_id.ids
return {
'name': _('Vendor Bills'),
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'view_mode': 'tree,form',
'domain': [('id', 'in', invoices)],
'domain': [('id', 'in', moves)],
}
# @api.depends('available_payment_method_line_ids')
# def _compute_payment_method_line_id(self):
# ''' Compute the 'payment_method_line_id' field.
# This field is not computed in '_compute_payment_method_line_fields' because it's a stored editable one.
# '''
# for pay in self:
# available_payment_method_lines = pay.available_payment_method_line_ids
#
# # Select the first available one by default.
# if pay.payment_method_id in available_payment_method_lines:
# pay.payment_method_id = pay.payment_method_id
# elif available_payment_method_lines:
# pay.payment_method_id = available_payment_method_lines[0]._origin
# else:
# pay.payment_method_id = False
def action_accounting_transfer(self):
for rec in self:
validation_setting = self.env["family.validation.setting"].search([], limit=1)

View File

@ -78,6 +78,7 @@ class PaymentOrders(models.Model):
rec.state = 'waiting_gm'
def create_entry(self):
#todo add invoice type to the move
for rec in self:
if not rec.journal_id:
raise UserError(_("Please select a journal before creating the entry."))
@ -103,10 +104,6 @@ class PaymentOrders(models.Model):
def action_deposit(self):
for rec in self:
if rec.type == "seasonal_services":
rec.seasonal_requests_ids.action_accounting_approve()
elif rec.type == "services":
rec.service_requests_ids.write({'state': 'action_accounting_approve'})
rec.state = 'done'
def action_gm_approval(self):

View File

@ -163,6 +163,7 @@ class ServiceRequest(models.Model):
required_attach = fields.Boolean(string='Required Attach', related='service_cat.required_attach')
state = fields.Selection(selection=[
('draft', 'Draft'),
('waiting_family', 'Waiting Family'),
('researcher', 'Researcher'),
('waiting_approve', 'Waiting for Operation Manager'),
('first_approve', 'Waiting for Branch Manager'),
@ -218,6 +219,7 @@ class ServiceRequest(models.Model):
('none', 'None'),
('waiting', 'Waiting Payment'),
('done', 'Done Payment'), ], copy=False, compute="_compute_payment_order_state", store=True)
total_moves = fields.Integer(string="Total Move", compute='_get_total_move_lines')
@api.depends('payment_order_id', 'payment_order_id.state', 'vendor_bill', 'vendor_bill.state')
def _compute_payment_order_state(self):
@ -489,6 +491,10 @@ class ServiceRequest(models.Model):
for rec in self:
rec.state = 'researcher'
def action_return_to_family(self):
for rec in self:
rec.state = 'waiting_family'
def action_researcher_send_request(self):
for rec in self:
if not rec.requested_service_amount or rec.requested_service_amount <= 0:
@ -967,7 +973,8 @@ class ServiceRequest(models.Model):
@api.depends('family_category')
def _compute_available_service_cats(self):
for rec in self:
domain = [('service_type', '!=', 'main_service'), ('benefit_category_ids', 'in', [rec.family_category.id])]
domain = [('is_seasonal_service', '=', False), ('service_type', '!=', 'main_service'),
('benefit_category_ids', 'in', [rec.family_category.id])]
if rec.family_id.property_type not in ['ownership', 'ownership_shared', 'charitable']:
domain.append(('service_type', '!=', 'home_restoration'))
else:
@ -1049,3 +1056,26 @@ class ServiceRequest(models.Model):
'invoice_line_ids': line_ids,
})
self.vendor_bill = vendor_bill
def _get_total_move_lines(self):
for rec in self:
if self.service_cat.payment_method == "payment_order":
moves = self.payment_order_id.move_id
elif self.service_cat.payment_method == "invoice":
moves = self.vendor_bill
rec.total_moves = len(moves)
def action_open_related_move_records(self):
if self.service_cat.payment_method == "payment_order":
moves = self.payment_order_id.move_id.ids
elif self.service_cat.payment_method == "invoice":
moves = self.vendor_bill.ids
return {
'name': _('Vendor Bills'),
'type': 'ir.actions.act_window',
'res_model': 'account.move',
'view_mode': 'tree,form',
'domain': [('id', 'in', moves)],
}

View File

@ -530,7 +530,7 @@
<field name="branch_family_id"
readonly="1"
options="{'no_create': True, 'no_create_edit': True}"/>
<field name="branch_has_employees"/>
<field name="branch_has_employees" invisible="1"/>
<field name="district_id"
attrs="{'readonly':[('state', 'not in', ['draft', 'new', 'complete_info'])]}"
options="{'no_create': True, 'no_create_edit': True}"
@ -1920,6 +1920,8 @@
<field name="sms_phone"/>
<field name="phone"/>
<field name="code"/>
<field name="branch_custom_id"/>
<field name="branch_family_id"/>
<group string="Group By">
<filter string="Branch" name="branch_custom_id" context="{'group_by': 'branch_custom_id'}"/>
<filter string="District" name="district_id" context="{'group_by': 'district_id'}"/>

View File

@ -66,11 +66,11 @@
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_open_related_invoice_records"
<button name="action_open_related_move_records"
class="oe_stat_button"
icon="fa-file-text-o"
type="object" attrs="{'invisible': [('total_invoices', '=', 0)]}">
<field name="total_invoices" widget="statinfo" string="Total Moves"/>
type="object" attrs="{'invisible': [('total_moves', '=', 0)]}">
<field name="total_moves" widget="statinfo" string="Total Moves"/>
</button>
</div>
<field name="payment_state" invisible="1"/>
@ -106,8 +106,7 @@
<field name="date" required="1" attrs="{'readonly':[('state', '!=', 'draft')]}"/>
<field name="payment_order_id" readonly="1"
attrs="{'invisible': [('payment_order_id', '=', False)]}"/>
<field name="move_id" readonly="1"
attrs="{'invisible': [('move_id', '=', False)]}"/>
<field name="move_id" invisible="1"/>
</group>
</group>
@ -163,7 +162,8 @@
</field>
</page>
<page string="Families">
<field name="family_ids" domain="[('id', 'in', family_domain_ids)]"/>
<field name="family_ids" attrs="{'readonly':[('state', '!=', 'draft')]}"
domain="[('id', 'in', family_domain_ids)]"/>
</page>
</notebook>
</sheet>

View File

@ -315,16 +315,17 @@
<field name="member_location"/>
<field name="member_phone"/>
<field name="benefit_id"/>
<field name="benefit_id" options="{'include_related_fields': ['father_name', 'father_family_name']}"/>
<field name="benefit_id"
options="{'include_related_fields': ['father_name', 'father_family_name']}"/>
<field name="member_id_number"/>
<field name="state"/>
<field name="total_member_service_requests"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click #{record.member_status.raw_value == 'non_benefit' ? 'non_benefit_member_card' : ''}"
t-attf-style="background: linear-gradient(135deg, #{record.member_status.raw_value == 'non_benefit' ? '#fff5f5' : '#ffffff'} 0%, #{record.member_status.raw_value == 'non_benefit' ? '#ffe0e0' : '#f8f9fa'} 100%); border: 2px solid #{record.member_status.raw_value == 'non_benefit' ? '#dc3545' : '#198754'}; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); transition: all 0.3s ease; margin-bottom: 15px; overflow: hidden; position: relative; min-height: 280px; opacity: #{record.member_status.raw_value == 'non_benefit' ? '0.85' : '1'};">
<div t-attf-class="oe_kanban_global_click #{record.member_status.raw_value == 'non_benefit' ? 'non_benefit_member_card' : ''}"
t-attf-style="background: linear-gradient(135deg, #{record.member_status.raw_value == 'non_benefit' ? '#fff5f5' : '#ffffff'} 0%, #{record.member_status.raw_value == 'non_benefit' ? '#ffe0e0' : '#f8f9fa'} 100%); border: 2px solid #{record.member_status.raw_value == 'non_benefit' ? '#dc3545' : '#198754'}; border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); transition: all 0.3s ease; margin-bottom: 15px; overflow: hidden; position: relative; min-height: 280px; opacity: #{record.member_status.raw_value == 'non_benefit' ? '0.85' : '1'};">
<div t-attf-style="
background: linear-gradient(135deg,
#{record.relationn_type.raw_value == 'son'
@ -342,11 +343,12 @@
padding: 12px 15px 70px 15px;
font-weight: 700;
position: relative;">
<div style="position: absolute; top: 8px; right: 15px; left: 15px; display: flex; justify-content: space-between; align-items: center; z-index: 1;">
<div style="background: rgba(255,255,255,0.97); padding: 6px 12px; border-radius: 8px; font-size: 11px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<i class="fa fa-id-card" style="color: #666; margin-left: 4px; font-size: 12px;"/>
<i class="fa fa-id-card"
style="color: #666; margin-left: 4px; font-size: 12px;"/>
<span style="color: #333;">
<t t-if="record.member_id_number.raw_value">
<field name="member_id_number"/>
@ -358,19 +360,22 @@
<div>
<t t-if="record.member_status.raw_value == 'non_benefit'">
<div style="background: rgba(255,255,255,0.97); color: #dc3545; padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<i class="fa fa-times-circle" style="color: #dc3545; margin-left: 4px; font-size: 13px;"/>
<i class="fa fa-times-circle"
style="color: #dc3545; margin-left: 4px; font-size: 13px;"/>
<span>غير مستفيد</span>
</div>
</t>
<t t-elif="record.member_status.raw_value == 'benefit'">
<div style="background: rgba(255,255,255,0.97); color: #198754; padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<i class="fa fa-check-circle" style="color: #198754; margin-left: 4px; font-size: 13px;"/>
<i class="fa fa-check-circle"
style="color: #198754; margin-left: 4px; font-size: 13px;"/>
<span>مستفيد</span>
</div>
</t>
<t t-else="">
<div style="background: rgba(255,255,255,0.97); color: #6c757d; padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<i class="fa fa-question-circle" style="color: #6c757d; margin-left: 4px; font-size: 13px;"/>
<i class="fa fa-question-circle"
style="color: #6c757d; margin-left: 4px; font-size: 13px;"/>
<span>غير محدد</span>
</div>
</t>
@ -378,32 +383,37 @@
</div>
<div style="position: absolute; top: 43px; right: 15px; left: 15px; display: flex; justify-content: space-between; align-items: center; z-index: 1;">
<div style="background: rgba(255,255,255,0.97); padding: 6px 12px; border-radius: 8px; font-size: 13px; font-weight: 800; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<i t-attf-class="fa #{record.relationn_type.raw_value == 'son' ? 'fa-male' : 'fa-female'}"
t-attf-style="color: #{record.relationn_type.raw_value == 'son'
t-attf-style="color: #{record.relationn_type.raw_value == 'son'
? '#5E97C7'
: record.relationn_type.raw_value == 'daughter'
? '#DD7FA8'
: '#9D87D3'};
margin-left: 4px; font-size: 14px;"/>
<span t-attf-style="color: #{record.relationn_type.raw_value == 'son'
? '#5E97C7'
: record.relationn_type.raw_value == 'daughter'
? '#DD7FA8'
: '#9D87D3'};">
<t t-if="record.relationn.raw_value"><field name="relationn"/></t>
<t t-if="record.relationn.raw_value">
<field name="relationn"/>
</t>
<t t-else="">غير محدد</t>
</span>
</div>
<div style="font-size: 12px; opacity: 0.9;">
<i class="fa fa-birthday-cake" style="margin-left: 4px;"/>
<span>العمر: </span>
<span>العمر:</span>
<span style="font-weight: 800;">
<t t-if="record.age.raw_value"><field name="age"/> سنة</t>
<t t-if="record.age.raw_value">
<field name="age"/>
سنة
</t>
<t t-else="">--</t>
</span>
</div>
@ -411,7 +421,7 @@
</div>
<div style="padding: 10px; background: white;">
<div style="margin-bottom: 8px; text-align: center;">
<h4 style="margin: 0; font-size: 16px; font-weight: 700; color: #212529; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<t t-if="record.name.raw_value">
@ -424,8 +434,9 @@
</div>
<div style="display: flex; align-items: center; font-size: 11px; margin-bottom: 6px;">
<i class="fa fa-home" style="color: #dc3545; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">السكن: </span>
<i class="fa fa-home"
style="color: #dc3545; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">السكن:</span>
<span style="color: #212529; margin-right: 4px; font-weight: 500;">
<t t-if="record.member_location.raw_value">
<field name="member_location"/>
@ -437,8 +448,9 @@
</div>
<div style="display: flex; align-items: center; font-size: 11px; margin-bottom: 6px;">
<i class="fa fa-graduation-cap" style="color: #6f42c1; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">التعليم: </span>
<i class="fa fa-graduation-cap"
style="color: #6f42c1; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">التعليم:</span>
<span style="color: #212529; margin-right: 4px; font-weight: 500;">
<t t-if="record.education_status.raw_value">
<field name="education_status"/>
@ -451,8 +463,9 @@
<div style="display: flex; justify-content: space-between; align-items: center; font-size: 11px; margin-bottom: 6px;">
<div style="display: flex; align-items: center;">
<i class="fa fa-briefcase" style="color: #ff9800; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">يعمل: </span>
<i class="fa fa-briefcase"
style="color: #ff9800; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">يعمل:</span>
<span style="margin-right: 4px;">
<t t-if="record.is_work.raw_value">
<span style="background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%); color: #2e7d32; padding: 2px 6px; border-radius: 8px; font-weight: 700; font-size: 10px; border: 1px solid #4caf50;">
@ -464,10 +477,11 @@
</t>
</span>
</div>
<div style="display: flex; align-items: center;">
<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="margin-right: 4px;">
<t t-if="record.member_income.raw_value and record.member_income.raw_value > 0">
<span style="background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%); color: #2e7d32; padding: 2px 6px; border-radius: 8px; font-weight: 700; font-size: 10px; border: 1px solid #4caf50;">
@ -483,11 +497,13 @@
<div style="margin-bottom: 8px;">
<div style="display: flex; align-items: center; font-size: 11px; margin-bottom: 4px;">
<i class="fa fa-phone" style="color: #17a2b8; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">الجوال: </span>
<i class="fa fa-phone"
style="color: #17a2b8; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">الجوال:</span>
<span style="color: #212529; margin-right: 4px; font-weight: 500;">
<t t-if="record.member_phone.raw_value and record.member_phone.raw_value.substr(0,3) === '966'">
0<t t-esc="record.member_phone.raw_value.substr(3)"/>
0
<t t-esc="record.member_phone.raw_value.substr(3)"/>
</t>
<t t-elif="record.member_phone.raw_value">
<field name="member_phone"/>
@ -497,19 +513,21 @@
</t>
</span>
</div>
<div style="display: flex; align-items: center; font-size: 11px;">
<i class="fa fa-users" style="color: #795548; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">الأسرة: </span>
<i class="fa fa-users"
style="color: #795548; margin-left: 4px; font-size: 12px; width: 16px;"/>
<span style="font-weight: 600; color: #495057;">الأسرة:</span>
<span style="color: #212529; margin-right: 4px; font-weight: 500; display: flex; align-items: center; gap: 4px; overflow: hidden;">
<t t-if="record.benefit_id.raw_value">
<t t-set="display_text" t-value="record.benefit_id.value || record.benefit_id.display_name"/>
<t t-set="display_text"
t-value="record.benefit_id.value || record.benefit_id.display_name"/>
<t t-set="parts" t-value="display_text.split(' - ')"/>
<span style="background: #e8f5e8; color: #2e7d32; padding: 2px 6px; border-radius: 4px; font-weight: 700; font-size: 10px;">
<t t-esc="parts[0]"/>
</span>
<t t-if="parts.length > 1">
<t t-set="name_words" t-value="parts[1].split(' ')"/>
<span style="font-size: 11px;">
@ -530,7 +548,7 @@
</div>
<a name="action_open_related_member_service_requests" type="object"
style="
style="
display: block;
width: 100%;
text-decoration: none;
@ -541,8 +559,8 @@
transition: transform 0.15s ease, box-shadow 0.15s ease;
box-shadow: 0 2px 6px rgba(0,0,0,0.06);
"
onmouseover="this.style.transform='translateY(-4px)'; this.style.boxShadow='0 10px 20px rgba(0,0,0,0.12)'"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 6px rgba(0,0,0,0.06)';">
onmouseover="this.style.transform='translateY(-4px)'; this.style.boxShadow='0 10px 20px rgba(0,0,0,0.12)'"
onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 6px rgba(0,0,0,0.06)';">
<div style="
display:flex;
@ -565,11 +583,14 @@
border: 1px solid rgba(25,135,84,0.12);
font-size:14px;
">
<i class="fa fa-file-text-o" style="color:#198754; font-size:14px;"></i>
<i class="fa fa-file-text-o"
style="color:#198754; font-size:14px;"></i>
</div>
<div style="display:flex; flex-direction:column; line-height:1;">
<div style="font-size:13px; font-weight:700; color:#155724;">طلبات الخدمة</div>
<div style="font-size:13px; font-weight:700; color:#155724;">طلبات
الخدمة
</div>
<div style="font-size:11px; color:#3b3b3b; opacity:0.85;">
<t t-if="record.total_member_service_requests.raw_value and record.total_member_service_requests.raw_value &gt; 0">
عرض التفاصيل
@ -596,7 +617,8 @@
<t t-esc="record.total_member_service_requests.raw_value"/>
</span>
</t>
<i class="fa fa-chevron-left" style="color:#9aa5a0; font-size:12px;"></i>
<i class="fa fa-chevron-left"
style="color:#9aa5a0; font-size:12px;"></i>
</div>
</div>
</a>
@ -618,7 +640,8 @@
<field name="member_second_name"/>
<field name="member_third_name"/>
<field name="member_family_name"/>
<field name="benefit_id"/>
<field name="benefit_id"
filter_domain="['|', ('benefit_id.name', 'ilike', self), ('benefit_id.code', 'ilike', self)]"/>
<group string="Group By">
<filter string="State" name="state" context="{'group_by': 'state'}"/>
<filter string="Benefit" name="benefit_id" context="{'group_by': 'benefit_id'}"/>

View File

@ -63,7 +63,8 @@
<field name="payment_order_date" readonly="1"/>
<field name="type" readonly="1"/>
<field name="accountant_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="journal_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="journal_id"
attrs="{'readonly': [('state', 'in', ['done'])]}"/>
</group>
<group>
<field name="payment_order_description" readonly="1"/>

View File

@ -7,12 +7,17 @@
<field name="arch" type="xml">
<form string="Service Request">
<header>
<button name="action_send_to_researcher" type="object" states="draft"
<button name="action_send_to_researcher" type="object" states="draft,waiting_family"
string="Send" class="oe_highlight"/>
<button name="action_researcher_send_request" type="object" states="researcher"
string="Researcher Approval" class="oe_highlight"
groups="odex_benefit.group_benefit_researcher"
/>
<button name="action_return_to_family" type="object" states="researcher"
string="Return to Family" class="btn btn-warning"
groups="odex_benefit.group_benefit_researcher"
/>
<button name="action_refuse" type="object"
string="Refuse" class="btn btn-danger"
states="researcher" groups="odex_benefit.group_benefit_researcher"/>
@ -157,6 +162,14 @@
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_open_related_move_records"
class="oe_stat_button"
icon="fa-file-text-o"
type="object" attrs="{'invisible': [('total_moves', '=', 0)]}">
<field name="total_moves" widget="statinfo" string="Total Moves"/>
</button>
</div>
<field name="is_payment_order_done" invisible="1"/>
<field name="payment_order_state" invisible="1"/>
<widget name="web_ribbon" title="Payment Done" bg_color="bg-success"
@ -193,9 +206,9 @@
<field name="accountant_id"
attrs="{'invisible':[('state','in',['draft','researcher','waiting_approve','first_approve','gm_assistant'])]}"/>
<field name="payment_order_id" readonly="1"
groups="odex_benefit.group_benefit_accounting_accept" invisible="1"/>
<field name="vendor_bill" readonly="1"
attrs="{'invisible':[('vendor_bill','=',False)]}"/>
attrs="{'invisible':[('payment_order_id','=',False)]}"
groups="odex_benefit.group_benefit_accounting_accept"/>
<field name="vendor_bill" invisible="1"/>
<field name="is_service_producer" invisible="1"/>
<field name="service_producer_id"
attrs="{'invisible':[('is_service_producer','=',False)],'readonly':[('state','not in',['draft','researcher','waiting_approve'])]}"/>