[IMP] odex_benefit: IMP benefit

This commit is contained in:
younes 2025-12-15 13:14:55 +01:00
parent 3a742a0496
commit c7afd786f1
7 changed files with 258 additions and 19 deletions

View File

@ -628,9 +628,9 @@ class GrantBenefitProfile(models.Model):
def _compute_total_families(self):
for record in self:
record.total_father_families = self.search_count([('father_id_number','=',self.father_id_number),('id','!=',self.id)])
record.total_mother_families = self.search_count([('mother_id_number','=',self.mother_id_number),('id','!=',self.id)])
record.total_replacement_mother_families = self.search_count([('replacement_mother_id_number','=',self.replacement_mother_id_number),('id','!=',self.id)])
record.total_father_families = self.search_count([('father_id_number','=',record.father_id_number),('id','!=',record.id)])
record.total_mother_families = self.search_count([('mother_id_number','=',record.mother_id_number),('id','!=',record.id)])
record.total_replacement_mother_families = self.search_count([('replacement_mother_id_number','=',record.replacement_mother_id_number),('id','!=',record.id)])
def action_open_related_father_families(self):
self.ensure_one()
@ -827,6 +827,7 @@ class GrantBenefitProfile(models.Model):
record.required_attach = 'true'
def get_attached_domain(self):
self.ensure_one()
visit_location = self.env['visit.location'].search([('benefit_id', '=', self.id)]).ids
service_requests = self.env['service.request'].search([('family_id', '=', self.id)]).ids
family_complaints = self.env['family.complaints'].search([('family_id', '=', self.id)]).ids
@ -865,7 +866,7 @@ class GrantBenefitProfile(models.Model):
def _compute_attached_docs_count(self):
Attachment = self.env['ir.attachment']
for benefit in self:
benefit.doc_count = Attachment.search_count(self.get_attached_domain())
benefit.doc_count = Attachment.search_count(benefit.get_attached_domain())
def view_attachments(self):
action = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment')
@ -1881,20 +1882,21 @@ class GrantBenefitProfile(models.Model):
@api.depends('name')
def _compute_qr_code(self):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=15,
border=4,
)
name = self.name
qr.add_data(name)
qr.make(fit=True)
img = qr.make_image()
temp = BytesIO()
img.save(temp, format="PNG")
qr_image = base64.b64encode(temp.getvalue())
self.qr_code = qr_image
for rec in self:
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=15,
border=4,
)
name = rec.name
qr.add_data(name)
qr.make(fit=True)
img = qr.make_image()
temp = BytesIO()
img.save(temp, format="PNG")
qr_image = base64.b64encode(temp.getvalue())
rec.qr_code = qr_image
@api.onchange('bank_id')
def _compute_prefix_iban(self):

View File

@ -2,7 +2,7 @@ from odoo import fields, models, api, _
from odoo.exceptions import UserError, ValidationError
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from odoo.tools import html_escape
class ServiceRequest(models.Model):
_name = 'service.request'
@ -204,6 +204,125 @@ class ServiceRequest(models.Model):
total_moves = fields.Integer(string="Total Move", compute='_get_total_move_lines')
return_reason_id = fields.Many2one("return.reason", string="Return Reason")
agree_terms = fields.Boolean(string="I agree to the Terms and Conditions", default=False, )
related_information_html = fields.Html(string="Related Information",
compute='_compute_related_information_html',store=True,)
@api.depends('service_cat', 'family_id', 'member_id', 'benefit_type')
def _compute_related_information_html(self):
for rec in self:
if not rec.service_cat:
rec.related_information_html = f"""
<div class="o_form_sheet_not_found">
<div class="o_form_sheet_not_found_icon">📋</div>
<h4>{_("No service selected")}</h4>
<p>{_("Please select a service category to view related information.")}</p>
</div>
"""
continue
family_rows = []
member_rows = []
for ir_field in rec.service_cat.family_related_fields:
row = rec._get_field_display_value(rec.family_id, ir_field)
if row:
family_rows.append(row)
if rec.benefit_type == 'member' and rec.member_id:
for ir_field in rec.service_cat.member_related_fields:
row = rec._get_field_display_value(rec.member_id, ir_field)
if row:
member_rows.append(row)
family_table = rec._build_info_table(_("Family Information"), family_rows)
member_table = rec._build_info_table(_("Member Information"), member_rows) if member_rows else ""
rec.related_information_html = f"""
<div class="info-container">
{family_table}
{member_table}
</div>
"""
def _build_info_table(self, title, rows):
if not rows:
return f"""
<div class="info-section">
<h3>{title}</h3>
<div class="info-empty">{_("No information available for this section.")}</div>
</div>
"""
return f"""
<div class="info-section">
<h3>{title}</h3>
<table class="table info-table">
<thead>
<tr>
<th>{_("Field Name")}</th>
<th>{_("Value")}</th>
</tr>
</thead>
<tbody>
{''.join(rows)}
</tbody>
</table>
</div>
"""
def _get_field_display_value(self, record, ir_field):
if not record or not ir_field:
return ""
field_name = ir_field.name
field = record._fields.get(field_name)
if not field:
return ""
value = record[field_name]
label = html_escape(ir_field.field_description or field_name)
if value in (False, None, '') or (hasattr(value, '__len__') and len(value) == 0):
display = '<span class="info-empty">—</span>'
elif field.type == 'boolean':
display = f"<span class='badge badge-success'>✓ {_('Yes')}</span>" if value else f"<span class='badge badge-danger'>✗ {_('No')}</span>"
elif field.type == 'selection':
sel_dict = dict(field._description_selection(record.env))
display_val = sel_dict.get(value, value)
display = f'<span class="badge">{html_escape(str(display_val or ""))}</span>'
elif field.type == 'many2one':
display = f'<strong>{html_escape(value.name or "")}</strong>'
elif field.type == 'many2many':
if field.comodel_name == 'ir.attachment':
if not value:
display = '<span class="info-empty">—</span>'
else:
links = []
for attach in value:
filename = attach.name or _("Unnamed file")
url = f"/web/content/{attach.id}?download=true"
links.append(f'<a class="file-link" href="{url}" target="_blank">📎 {html_escape(filename)}</a>')
display = '<br/>'.join(links)
else:
badges = [f'<span class="badge badge-secondary">{html_escape(r.name or "-")}</span>' for r in value]
display = ' '.join(badges) or '<span class="info-empty">—</span>'
elif field.type == 'one2many':
items = [f'<div style="margin:8px 0; padding:6px 0; border-bottom:1px dotted #ddd;">• {html_escape(r.display_name or r.name or "-")}</div>' for r in value[:20]]
more = f'<div style="color:#875A92; font-weight:600; margin-top:10px;">... {_("and")} {len(value)-20} {_("more records")}</div>' if len(value) > 20 else ""
display = ''.join(items) + more or '<span class="info-empty">—</span>'
elif field.type == 'binary':
if value:
filename = getattr(record, f"{field_name}_fname", None) or _("Download file")
url = f"/web/content/{record._name}/{record.id}/{field_name}?download=true"
display = f'<a class="file-link" href="{url}" target="_blank">📎 {html_escape(filename)}</a>'
else:
display = '<span class="info-empty">—</span>'
else:
display = html_escape(str(value))
return f"""
<tr>
<td class="info-label">{label}</td>
<td class="info-value">{display}</td>
</tr>
"""
@api.depends('payment_order_ids')
def _compute_payment_order(self):

View File

@ -113,6 +113,30 @@ class ServicesSettings(models.Model):
('payment_order', 'Payment Order'),
('invoice', 'Invoice'),
], string='Payment Method',default="payment_order")
family_related_fields = fields.Many2many(
comodel_name='ir.model.fields',
relation='services_settings_family_field_rel',
column1='services_settings_id',
column2='field_id',
string='Related Family Fields',
domain="[('model_id.model', '=', 'grant.benefit')]",
help="Select fields from the Family profile (grant.benefit) to display in the service request."
)
member_related_fields = fields.Many2many(
comodel_name='ir.model.fields',
relation='services_settings_member_field_rel',
column1='services_settings_id',
column2='field_id',
string='Related Member Fields',
domain="[('model_id.model', '=', 'family.member')]",
help="Select fields from the Member profile to display only when the service is for a member."
)
@api.onchange('benefit_type')
def _onchange_benefit_type(self):
if self.benefit_type != 'member':
self.member_related_fields = [(5, 0, 0)]

View File

@ -0,0 +1,77 @@
.info-container {
font-family: inherit;
}
.info-container .info-section {
margin-bottom: 32px;
}
.info-container .info-section h3 {
color: #000000;
font-size: 15px;
font-weight: 600;
margin: 0 0 16px 0;
padding-bottom: 8px;
display: inline-block;
}
.info-container .info-table {
width: 70%;
overflow: hidden;
}
.info-container .info-table th {
background-color: #EEE;
font-weight: 600;
font-size: 15px;
}
.info-container .info-table td {
border-bottom: 1px solid #e9ecef;
}
.info-container .info-table tr:last-child td {
border-bottom: none;
}
.info-container .info-table tr:hover td {
background-color: #f8f5f9;
}
.info-container .info-value {
color: #212529;
line-height: 1.6;
}
.info-container .badge {
padding: 5px 12px;
border-radius: 20px;
font-size: 13px;
font-weight: 500;
display: inline-block;
margin: 3px 6px 3px 0;
}
.info-container .badge-success { background:#d4edda; color:#155724; }
.info-container .badge-danger { background:#f8d7da; color:#721c24; }
.info-container .badge-info { background:#d1ecf1; color:#0c5460; }
.info-container .badge-secondary{ background:#e2e3e5; color:#383d41; }
.info-container .file-link {
color: #875A92;
text-decoration: none;
font-weight: 500;
}
.info-container .file-link:hover {
text-decoration: underline;
}
.info-container .info-empty {
color: #adb5bd;
font-style: italic;
text-align: center;
background: #f8f9fa;
}
.o_form_sheet_not_found {
text-align:center;
padding:50px;
background:#f8f9fa;
border:2px dashed #dee2e6;
border-radius:12px;
color:#6c757d;
}
.o_form_sheet_not_found .o_form_sheet_not_found_icon {
font-size:48px;
opacity:0.3;
margin-bottom:16px;
}

View File

@ -444,6 +444,11 @@
</group>
</group>
</page>
<page string="Related Information">
<group>
<field name="related_information_html" nolabel="1" readonly="1"/>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">

View File

@ -217,6 +217,17 @@
</group>
</group>
</page>
<page string="Related Information">
<group>
<group>
<field name="family_related_fields" widget="many2many_tags"/>
</group>
<group>
<field name="member_related_fields" widget="many2many_tags"
attrs="{'invisible': [('benefit_type', '!=', 'member')]}"/>
</group>
</group>
</page>
</notebook>
</sheet>
</form>

View File

@ -3,6 +3,7 @@
<template id="custom_assets_backend" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" type="text/scss" href="/odex_benefit/static/src/scss/custom_style.scss"/>
<link rel="stylesheet" type="text/scss" href="/odex_benefit/static/src/css/backend_style.css"/>
</xpath>
</template>
</odoo>