Merge pull request #5848 from expsa/email_t

email_tem
This commit is contained in:
esam-sermah 2025-12-28 15:12:58 +03:00 committed by GitHub
commit 897bbda9c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 336 additions and 11 deletions

View File

@ -11,6 +11,7 @@
"security/ir.model.access.csv",
"data/sequence.xml",
"views/annual_request_views.xml",
"views/addendum_views.xml",
"views/purchase_requisition.xml",
@ -20,6 +21,7 @@
"views/annual_rfq_views.xml",
"views/report_annual_rfq.xml",
"data/mail_template_annual_rfq.xml",
'data/mail_direct.xml',
"views/select_reason_rfq_views.xml",
"wizard/annual_cancel_wizard_view.xml"

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="email_template_direct_manager_x" model="mail.template">
<field name="name">Annual Request - Direct Manager Approval</field>
<field name="model_id" ref="odex25_annual_purchase.model_odx_annual_request"/>
<field name="subject">Annual Purchase Request #${object.name} - Action Required</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px; font-family: Arial, sans-serif;">
<p>Dear <strong>${object.employee_id.parent_id.name or 'Manager'}</strong>,</p>
<p>You have an <strong>Annual Purchase Request</strong> <a href="${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form"
>
${object.name}
</a> pending your approval:</p>
<br/>
</div>
</field>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.employee_id.lang}</field>
</record>
<record id="email_template_purchase_manager" model="mail.template">
<field name="name">Annual Request - Purchase Manager Approval</field>
<field name="model_id" ref="odex25_annual_purchase.model_odx_annual_request"/>
<field name="subject">Annual Purchase Request #${object.name} - Approval Required</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px; font-family: Arial, sans-serif;">
<p>Dear <strong>Purchase Manager</strong>,</p>
<p>You have an <strong>Annual Purchase Request</strong> <a href="${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form"
>
${object.name}
</a> pending your approval:</p>
<br/>
</div>
</field>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.employee_id.lang}</field>
</record>
<record id="email_template_services_manager" model="mail.template">
<field name="name">Annual Request - Services Manager Approval</field>
<field name="model_id" ref="odex25_annual_purchase.model_odx_annual_request"/>
<field name="subject">Annual Purchase Request #${object.name} - SSD Approval Required</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px; font-family: Arial, sans-serif;">
<p>Dear <strong>Services Manager</strong>,</p>
<p>You have an <strong>Annual Purchase Request</strong>
<a href="${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form">${object.name}</a>
pending your approval:
</p>
<br/>
</div>
</field>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.employee_id.lang}</field>
</record>
<record id="email_template_general_manager" model="mail.template">
<field name="name">Annual Request - General Manager Approval</field>
<field name="model_id" ref="odex25_annual_purchase.model_odx_annual_request"/>
<field name="subject">Annual Purchase Request #${object.name} - CEO Approval Required</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px; font-family: Arial, sans-serif;">
<p>Dear <strong>General Manager</strong>,</p>
<p>You have an <strong>Annual Purchase Request</strong>
<a href="${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form">${object.name}</a>
pending your approval:
</p>
<br/>
</div>
</field>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.employee_id.lang}</field>
</record>
<record id="email_template_committee_members" model="mail.template">
<field name="name">Annual Request - Committee Members Notification</field>
<field name="model_id" ref="odex25_annual_purchase.model_odx_annual_request"/>
<field name="subject">RFQs Created for Request #${object.name} - Please Review</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px; font-family: Arial, sans-serif;">
<p>Dear <strong>Committee Member</strong>,</p>
<p>RFQ requests have been created for Request No.
<a href="${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form">${object.name}</a>
</p>
<p>Please proceed to open and review the technical/financial offers</p>
<br/>
</div>
</field>
<field name="auto_delete" eval="True"/>
<field name="lang">${object.employee_id.lang}</field>
</record>
</data>
</odoo>

View File

@ -1338,3 +1338,116 @@ msgstr ""
#: model:mail.template,subject:odex25_annual_purchase.email_template_annual_rfq_new
msgid "Request for Quotation ${object.name or ''}"
msgstr "طلب عرض سعر ${object.name or ''}"
#. module: odex25_annual_purchase
#: model:mail.template,body_html:odex25_annual_purchase.email_template_direct_manager_x
msgid ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>Dear <strong>${object.employee_id.parent_id.name or 'Manager'}</strong>,</p>\n"
" <p>You have an <strong>Annual Purchase Request</strong> <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">\n"
" ${object.name}\n"
" </a> pending your approval:</p>\n"
" <br/>\n"
"\n"
"</div>\n"
" "
msgstr ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>عزيزي <strong>${object.employee_id.parent_id.name or 'المدير'}</strong>,</p>\n"
" <p>لديك طلب <strong>شراء سنوي</strong> <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">\n"
" ${object.name}\n"
" </a> بحاجة الى اعتماد:</p>\n"
" <br/>\n"
"\n"
"</div>\n"
" "
#. module: odex25_annual_purchase
#: model:mail.template,body_html:odex25_annual_purchase.email_template_purchase_manager
msgid ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>Dear <strong>Purchase Manager</strong>,</p>\n"
" <p>You have an <strong>Annual Purchase Request</strong> <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">\n"
" ${object.name}\n"
" </a> pending your approval:</p>\n"
" <br/>\n"
"</div>\n"
" "
msgstr ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>عزيزي <strong>مدير المشتريات</strong>,</p>\n"
" <p>لديك طلب <strong>شراء سنوي</strong> <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">\n"
" ${object.name}\n"
" </a> بحاجة الى اعتماد:</p>\n"
" <br/>\n"
"</div>\n"
" "
#. module: odex25_annual_purchase
#: model:mail.template,body_html:odex25_annual_purchase.email_template_services_manager
msgid ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>Dear <strong>Services Manager</strong>,</p>\n"
" <p>You have an <strong>Annual Purchase Request</strong>\n"
" <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">${object.name}</a>\n"
" pending your approval:\n"
" </p>\n"
" <br/>\n"
"</div>\n"
" "
msgstr ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>عزيزي <strong>مدير الخدمات</strong>,</p>\n"
" <p>لديك طلب <strong>شراء سنوي</strong>\n"
" <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">${object.name}</a>\n"
" بحاجة الى اعتماد:\n"
" </p>\n"
" <br/>\n"
"</div>\n"
" "
#. module: odex25_annual_purchase
#: model:mail.template,body_html:odex25_annual_purchase.email_template_general_manager
msgid ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>Dear <strong>General Manager</strong>,</p>\n"
" <p>You have an <strong>Annual Purchase Request</strong>\n"
" <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">${object.name}</a>\n"
" pending your approval:\n"
" </p>\n"
" <br/>\n"
"</div>\n"
" "
msgstr ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>عزيزي <strong>المدير التنفيذي</strong>,</p>\n"
" <p>لديك طلب <strong>شراء سنوي</strong>\n"
" <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">${object.name}</a>\n"
" بحاجة الى اعتماد:\n"
" </p>\n"
" <br/>\n"
"</div>\n"
" "
#. module: odex25_annual_purchase
#: model:mail.template,body_html:odex25_annual_purchase.email_template_committee_members
msgid ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>Dear <strong>Committee Member</strong>,</p>\n"
" <p>RFQ requests have been created for Request No.\n"
" <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">${object.name}</a>\n"
" </p>\n"
" <p>Please proceed to open and review the technical/financial offers</p>\n"
" <br/>\n"
"</div>\n"
" "
msgstr ""
"<div style=\"margin: 0px; padding: 0px; font-family: Arial, sans-serif;\">\n"
" <p>عزيزي <strong>عضو اللجنة</strong>,</p>\n"
" <p>تم إنشاء طلبات RFQ للطلب رقم\n"
" <a href=\"${object.get_base_url()}/web#id=${object.id}&amp;model=odx.annual.request&amp;view_type=form\">${object.name}</a>\n"
" </p>\n"
" <p>يرجى فتح العروض ومراجعة العروض الفنية/المالية</p>\n"
" <br/>\n"
"</div>\n"
" "

View File

@ -299,6 +299,16 @@ class AnnualPurchaseRequest(models.Model):
self._check_lines()
for rec in self:
rec.write({'state': 'to_manager'})
manager = rec.sudo().employee_id.parent_id
if manager and manager.user_id and manager.user_id.email:
try:
template = self.env.ref('odex25_annual_purchase.email_template_direct_manager_x')
template.send_mail(rec.id, force_send=True)
rec.message_post(body=_("Email sent to Direct Manager: %s") % manager.user_id.name)
except Exception as e:
rec.message_post(body=_("Failed to send email: %s") % str(e))
else:
rec.message_post(body=_("No direct manager email found"))
def action_to_draft(self):
for rec in self:
@ -310,10 +320,45 @@ class AnnualPurchaseRequest(models.Model):
if manager:
if manager.user_id.id == rec.env.uid:
rec.write({'state': 'procurement'})
purchase_group = self.env.ref('purchase.group_purchase_manager')
managers = self.env['res.users'].search([
('groups_id', '=', purchase_group.id),
('email', '!=', False)
])
if managers:
try:
template = self.env.ref('odex25_annual_purchase.email_template_purchase_manager')
first_manager = managers[0]
cc_emails = ','.join(managers[1:].mapped('email')) if len(managers) > 1 else ''
template.with_context(
default_email_to=first_manager.email,
default_email_cc=cc_emails
).send_mail(rec.id, force_send=True)
rec.message_post(body=_("Email sent to %s Purchase Manager(s)") % len(managers))
except Exception as e:
rec.message_post(body=_("Failed to send email: %s") % str(e))
else:
raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only !")%(rec.employee_id.parent_id.name))
raise UserError(
_("Sorry, The Approval For The Direct Manager '%s' Only !") % (rec.employee_id.parent_id.name))
else:
rec.write({'state': 'procurement'})
purchase_group = self.env.ref('purchase.group_purchase_manager')
managers = self.env['res.users'].search([
('groups_id', '=', purchase_group.id),
('email', '!=', False)
])
if managers:
try:
template = self.env.ref('odex25_annual_purchase.email_template_purchase_manager')
first_manager = managers[0]
cc_emails = ','.join(managers[1:].mapped('email')) if len(managers) > 1 else ''
template.with_context(
default_email_to=first_manager.email,
default_email_cc=cc_emails
).send_mail(rec.id, force_send=True)
rec.message_post(body=_("Email sent to %s Purchase Manager(s)") % len(managers))
except Exception as e:
rec.message_post(body=_("Failed to send email: %s") % str(e))
def action_manager_reject(self):
self.ensure_one()
@ -334,11 +379,27 @@ class AnnualPurchaseRequest(models.Model):
'sent_to_commitee': True,
'state': 'committee'
})
rec.rfq_ids.write({'state': 'committee'})
template = self.env.ref('odex25_annual_purchase.email_template_committee_members')
sent_count = 0
for member in rec.committe_members:
if member.email:
try:
template.with_context(
default_email_to=member.email
).send_mail(rec.id, force_send=True)
sent_count += 1
except Exception as e:
rec.message_post(body=_("Failed to send to %s: %s") % (member.name, str(e)))
rec.message_post(
body=_("Emails sent to %s/%s committee members") % (sent_count, len(rec.committe_members)))
else:
raise UserError("لا يمكن الإرسال إلى اللجنة لأن عدد RFQs يساوي صفر.")
def action_send_to_ssd(self):
self.write({'state':'ssd'})
@ -355,6 +416,23 @@ class AnnualPurchaseRequest(models.Model):
def action_ssd_approve(self):
if self.seo_approve:
self.write({'state': 'ceo'})
gm_group = self.env.ref('hr_base.group_general_manager')
managers = self.env['res.users'].search([
('groups_id', '=', gm_group.id),
('email', '!=', False)
])
if managers:
try:
template = self.env.ref('odex25_annual_purchase.email_template_general_manager')
first_manager = managers[0]
cc_emails = ','.join(managers[1:].mapped('email')) if len(managers) > 1 else ''
template.with_context(
default_email_to=first_manager.email,
default_email_cc=cc_emails
).send_mail(self.id, force_send=True)
self.message_post(body=_("Email sent to %s General Manager(s)") % len(managers))
except Exception as e:
self.message_post(body=_("Failed to send email: %s") % str(e))
else:
self.write({'state': 'purchase'})

View File

@ -6,7 +6,7 @@ class PurchaseRFQ(models.Model):
_name = 'annual.rfq'
_description = 'Request for Quotation'
_inherit = ['mail.thread', 'mail.activity.mixin']
_copy = False # تعطيل النسخ لحجب زر الاستنساخ
_copy = False
name = fields.Char(string='Reference Number', default='New', copy=False, tracking=True)
@ -412,10 +412,11 @@ class PurchaseRFQ(models.Model):
}
rline.write(vals)
def action_sign_rfq(self):
for rec in self:
rec._validate_lines_simple()
if self.source_request_ref.committee_enabled and self.no_of_approve < self.source_request_ref.min_approve:
if rec.source_request_ref.committee_enabled and rec.no_of_approve < rec.source_request_ref.min_approve:
raise ValidationError(
_("Sorry You cannot sign this quotation ,YOU NEED MORE COMMITTE MEMBERS TO choose it"))
@ -424,8 +425,44 @@ class PurchaseRFQ(models.Model):
rec.state = 'po'
if rec.source_request_ref.ssd_approve:
rec.source_request_ref.state = 'ssd'
services_group = self.env.ref('hr_base.group_services_manager')
managers = self.env['res.users'].search([
('groups_id', '=', services_group.id),
('email', '!=', False)
])
if managers:
try:
template = self.env.ref('odex25_annual_purchase.email_template_services_manager')
first_manager = managers[0]
cc_emails = ','.join(managers[1:].mapped('email')) if len(managers) > 1 else ''
template.with_context(
default_email_to=first_manager.email,
default_email_cc=cc_emails
).send_mail(rec.source_request_ref.id, force_send=True)
rec.source_request_ref.message_post(
body=_("Email sent to %s Services Manager(s)") % len(managers))
except Exception as e:
rec.source_request_ref.message_post(body=_("Failed to send email: %s") % str(e))
elif rec.source_request_ref.seo_approve:
rec.source_request_ref.state = 'ceo'
gm_group = self.env.ref('hr_base.group_general_manager')
managers = self.env['res.users'].search([
('groups_id', '=', gm_group.id),
('email', '!=', False)
])
if managers:
try:
template = self.env.ref('odex25_annual_purchase.email_template_general_manager')
first_manager = managers[0]
cc_emails = ','.join(managers[1:].mapped('email')) if len(managers) > 1 else ''
template.with_context(
default_email_to=first_manager.email,
default_email_cc=cc_emails
).send_mail(rec.source_request_ref.id, force_send=True)
rec.source_request_ref.message_post(
body=_("Email sent to %s General Manager(s)") % len(managers))
except Exception as e:
rec.source_request_ref.message_post(body=_("Failed to send email: %s") % str(e))
else:
rec.source_request_ref.state = 'purchase'
else:
@ -433,9 +470,6 @@ class PurchaseRFQ(models.Model):
'Cannot continue: the source document is Approval stage.'
))
@api.depends('line_ids.subtotal', 'line_ids.tax_amount')
def _compute_amounts(self):
for rec in self: