From d0353fcfe91378385a040616dc5e5182b237cbe9 Mon Sep 17 00:00:00 2001 From: esam Date: Sun, 28 Dec 2025 15:12:00 -0500 Subject: [PATCH] email_tem --- .../odex25_annual_purchase/__manifest__.py | 2 + .../data/mail_direct.xml | 98 +++++++++++++++ .../odex25_annual_purchase/i18n/ar_001.po | 113 ++++++++++++++++++ .../models/annual_request.py | 90 +++++++++++++- .../models/annual_rfq.py | 44 ++++++- 5 files changed, 336 insertions(+), 11 deletions(-) create mode 100644 odex25_purchase/odex25_annual_purchase/data/mail_direct.xml diff --git a/odex25_purchase/odex25_annual_purchase/__manifest__.py b/odex25_purchase/odex25_annual_purchase/__manifest__.py index 5a629b2b3..fc5cb08e3 100644 --- a/odex25_purchase/odex25_annual_purchase/__manifest__.py +++ b/odex25_purchase/odex25_annual_purchase/__manifest__.py @@ -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" diff --git a/odex25_purchase/odex25_annual_purchase/data/mail_direct.xml b/odex25_purchase/odex25_annual_purchase/data/mail_direct.xml new file mode 100644 index 000000000..223aea33a --- /dev/null +++ b/odex25_purchase/odex25_annual_purchase/data/mail_direct.xml @@ -0,0 +1,98 @@ + + + + + Annual Request - Direct Manager Approval + + Annual Purchase Request #${object.name} - Action Required + +
+

Dear ${object.employee_id.parent_id.name or 'Manager'},

+

You have an Annual Purchase Request + ${object.name} + pending your approval:

+
+ +
+
+ + ${object.employee_id.lang} +
+ + + Annual Request - Purchase Manager Approval + + Annual Purchase Request #${object.name} - Approval Required + +
+

Dear Purchase Manager,

+

You have an Annual Purchase Request + ${object.name} + pending your approval:

+
+
+
+ + ${object.employee_id.lang} +
+ + + Annual Request - Services Manager Approval + + Annual Purchase Request #${object.name} - SSD Approval Required + +
+

Dear Services Manager,

+

You have an Annual Purchase Request + ${object.name} + pending your approval: +

+
+
+
+ + ${object.employee_id.lang} +
+ + + Annual Request - General Manager Approval + + Annual Purchase Request #${object.name} - CEO Approval Required + +
+

Dear General Manager,

+

You have an Annual Purchase Request + ${object.name} + pending your approval: +

+
+
+
+ + ${object.employee_id.lang} +
+ + Annual Request - Committee Members Notification + + RFQs Created for Request #${object.name} - Please Review + +
+

Dear Committee Member,

+

RFQ requests have been created for Request No. + ${object.name} +

+

Please proceed to open and review the technical/financial offers

+
+
+
+ + ${object.employee_id.lang} +
+ + + + +
+
diff --git a/odex25_purchase/odex25_annual_purchase/i18n/ar_001.po b/odex25_purchase/odex25_annual_purchase/i18n/ar_001.po index 45b519fea..8fe31e4c4 100644 --- a/odex25_purchase/odex25_annual_purchase/i18n/ar_001.po +++ b/odex25_purchase/odex25_annual_purchase/i18n/ar_001.po @@ -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 "" +"
\n" +"

Dear ${object.employee_id.parent_id.name or 'Manager'},

\n" +"

You have an Annual Purchase Request \n" +" ${object.name}\n" +" pending your approval:

\n" +"
\n" +"\n" +"
\n" +" " +msgstr "" +"
\n" +"

عزيزي ${object.employee_id.parent_id.name or 'المدير'},

\n" +"

لديك طلب شراء سنوي \n" +" ${object.name}\n" +" بحاجة الى اعتماد:

\n" +"
\n" +"\n" +"
\n" +" " + +#. module: odex25_annual_purchase +#: model:mail.template,body_html:odex25_annual_purchase.email_template_purchase_manager +msgid "" +"
\n" +"

Dear Purchase Manager,

\n" +"

You have an Annual Purchase Request \n" +" ${object.name}\n" +" pending your approval:

\n" +"
\n" +"
\n" +" " +msgstr "" +"
\n" +"

عزيزي مدير المشتريات,

\n" +"

لديك طلب شراء سنوي \n" +" ${object.name}\n" +" بحاجة الى اعتماد:

\n" +"
\n" +"
\n" +" " + +#. module: odex25_annual_purchase +#: model:mail.template,body_html:odex25_annual_purchase.email_template_services_manager +msgid "" +"
\n" +"

Dear Services Manager,

\n" +"

You have an Annual Purchase Request\n" +" ${object.name}\n" +" pending your approval:\n" +"

\n" +"
\n" +"
\n" +" " +msgstr "" +"
\n" +"

عزيزي مدير الخدمات,

\n" +"

لديك طلب شراء سنوي\n" +" ${object.name}\n" +" بحاجة الى اعتماد:\n" +"

\n" +"
\n" +"
\n" +" " + +#. module: odex25_annual_purchase +#: model:mail.template,body_html:odex25_annual_purchase.email_template_general_manager +msgid "" +"
\n" +"

Dear General Manager,

\n" +"

You have an Annual Purchase Request\n" +" ${object.name}\n" +" pending your approval:\n" +"

\n" +"
\n" +"
\n" +" " +msgstr "" +"
\n" +"

عزيزي المدير التنفيذي,

\n" +"

لديك طلب شراء سنوي\n" +" ${object.name}\n" +" بحاجة الى اعتماد:\n" +"

\n" +"
\n" +"
\n" +" " + +#. module: odex25_annual_purchase +#: model:mail.template,body_html:odex25_annual_purchase.email_template_committee_members +msgid "" +"
\n" +"

Dear Committee Member,

\n" +"

RFQ requests have been created for Request No.\n" +" ${object.name}\n" +"

\n" +"

Please proceed to open and review the technical/financial offers

\n" +"
\n" +"
\n" +" " +msgstr "" +"
\n" +"

عزيزي عضو اللجنة,

\n" +"

تم إنشاء طلبات RFQ للطلب رقم\n" +" ${object.name}\n" +"

\n" +"

يرجى فتح العروض ومراجعة العروض الفنية/المالية

\n" +"
\n" +"
\n" +" " diff --git a/odex25_purchase/odex25_annual_purchase/models/annual_request.py b/odex25_purchase/odex25_annual_purchase/models/annual_request.py index 045dda1dd..71be5245a 100644 --- a/odex25_purchase/odex25_annual_purchase/models/annual_request.py +++ b/odex25_purchase/odex25_annual_purchase/models/annual_request.py @@ -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: @@ -308,12 +318,47 @@ class AnnualPurchaseRequest(models.Model): for rec in self: manager = rec.sudo().employee_id.parent_id if manager: - if manager.user_id.id == rec.env.uid : - rec.write({'state': 'procurement'}) - else: - raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only !")%(rec.employee_id.parent_id.name)) + 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)) 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'}) @@ -354,7 +415,24 @@ class AnnualPurchaseRequest(models.Model): def action_ssd_approve(self): if self.seo_approve: - self.write({'state':'ceo'}) + 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'}) diff --git a/odex25_purchase/odex25_annual_purchase/models/annual_rfq.py b/odex25_purchase/odex25_annual_purchase/models/annual_rfq.py index 6cf2e512d..b51d1ee06 100644 --- a/odex25_purchase/odex25_annual_purchase/models/annual_rfq.py +++ b/odex25_purchase/odex25_annual_purchase/models/annual_rfq.py @@ -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: