Merge pull request #5542 from expsa/anuul_rfq

Anuul rfq
This commit is contained in:
esam-sermah 2025-11-26 12:58:40 +03:00 committed by GitHub
commit 4bdcc9e27c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 147 additions and 54 deletions

View File

@ -1,3 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<!-- Placeholder for future email templates and activities --> <data noupdate="1">
<record id="email_template_annual_rfq_ar" model="mail.template">
<field name="name">Purchase Order: Send RFQ</field>
<field name="model_id" ref="odex25_annual_purchase.model_annual_rfq"/>
<field name="subject">طلب عرض سعر ${object.name or ''}</field>
<field name="body_html" type="html">
<div style="margin: 0px; padding: 0px;">
<p style="margin: 0px; padding: 0px; font-size: 13px; font-family: Arial, sans-serif; direction: rtl; text-align: right;">
عزيزي ${object.vendor_id.name}
% if object.vendor_id.parent_id:
(${object.vendor_id.parent_id.name})
% endif
<br/><br/>
يُرفق إليكم طلب عرض السعر الخاص بالطلب رقم <strong>${object.name}</strong>
% if object.partner_ref:
بالمرجع: ${object.partner_ref}
% endif
من شركة ${object.company_id.name}.
<br/><br/>
في حال وجود أي استفسارات، لا تترددوا في التواصل معنا.
<br/><br/>
مع أطيب التحيات،
</p>
</div></field>
<field name="report_template" ref="odex25_annual_purchase.action_report_annual_rfq"/>
<field name="report_name">${(object.name or '').replace('/','_')}</field>
<field name="lang">ar</field>
<field name="auto_delete" eval="True"/>
</record>
</data>
</odoo> </odoo>

View File

@ -173,11 +173,7 @@ msgstr "عدد المرفقات"
msgid "Can Create Agreement" msgid "Can Create Agreement"
msgstr "يمكن إنشاء اتفاقية" msgstr "يمكن إنشاء اتفاقية"
#. module: odex25_annual_purchase
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_odx_annual_addendum_form
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_odx_annual_request_form
msgid "Cancel"
msgstr "إلغاء"
#. module: odex25_annual_purchase #. module: odex25_annual_purchase
#: model:ir.model.fields.selection,name:odex25_annual_purchase.selection__odx_annual_addendum__state__cancel #: model:ir.model.fields.selection,name:odex25_annual_purchase.selection__odx_annual_addendum__state__cancel
@ -1042,15 +1038,6 @@ msgstr "هل أنت متأكد أنك تريد المتابعة؟"
#. module: odex25_annual_purchase
#: code:addons/odex25_annual_purchase/models/annual_rfq.py:0
#: model:ir.model,name:odex25_annual_purchase.model_annual_rfq
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_annual_rfq_form
#, python-format
msgid "Request for Quotation"
msgstr "طلب عرض سعر"
#. module: odex25_annual_purchase #. module: odex25_annual_purchase
@ -1274,3 +1261,34 @@ msgstr "تأكيد"
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_odx_annual_request_reason_wizard_form #: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_odx_annual_request_reason_wizard_form
msgid "Discard" msgid "Discard"
msgstr "إلغاء" msgstr "إلغاء"
#. module: odex25_annual_purchase
#: code:addons/odex25_annual_purchase/models/annual_rfq.py:0
#: model:ir.model,name:odex25_annual_purchase.model_annual_rfq
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_annual_rfq_form
#, python-format
msgid "Request for Quotation"
msgstr "طلب عرض سعر"
#. module: odex25_annual_purchase
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_refuse_reason_rfq_form
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_select_reason_rfq_form
msgid "Submit"
msgstr "إرسال"
#. module: odex25_annual_purchase
#: model:ir.model.fields.selection,name:odex25_annual_purchase.selection__odx_annual_request_reason_wizard__action_type__cancel
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_odx_annual_addendum_form
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_odx_annual_request_form
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_refuse_reason_rfq_form
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_select_reason_rfq_form
msgid "Cancel"
msgstr "إلغاء"
#. module: odex25_annual_purchase
#: model_terms:ir.ui.view,arch_db:odex25_annual_purchase.view_annual_rfq_form
msgid "Re-Send by Email"
msgstr "إعادة إرسال طلب عرض سعر"

View File

@ -43,7 +43,7 @@ class AnnualPurchaseRequest(models.Model):
vendor_id = fields.Many2one('res.partner', string="Selected Vendor", domain=[('supplier_rank','>',0)]) vendor_id = fields.Many2one('res.partner', string="Selected Vendor", domain=[('supplier_rank','>',0)])
currency_id = fields.Many2one('res.currency', string="Currency", default=lambda self: self.env.company.currency_id.id) currency_id = fields.Many2one('res.currency', string="Currency", default=lambda self: self.env.company.currency_id.id)
technical_notes = fields.Html(string="Technical Notes") technical_notes = fields.Html(string="Technical Notes")
financial_notes = fields.Html(string="Financial Notes", groups="purchase_requisition_custom.committe_member") financial_notes = fields.Html(string="Financial Notes")
committee_recommended_vendor_id = fields.Many2one('res.partner', string="Recommended Vendor", domain=[('supplier_rank','>',0)]) committee_recommended_vendor_id = fields.Many2one('res.partner', string="Recommended Vendor", domain=[('supplier_rank','>',0)])
committee_status = fields.Selection([ committee_status = fields.Selection([
('none','None'), ('none','None'),
@ -379,8 +379,6 @@ class AnnualPurchaseRequest(models.Model):
def action_create_agreement(self): def action_create_agreement(self):
for rec in self: for rec in self:
rec._check_lines() rec._check_lines()
if not rec.vendor_id:
raise UserError(_("Please set Selected Vendor."))
if rec.agreement_id: if rec.agreement_id:
raise UserError(_("Agreement already linked.")) raise UserError(_("Agreement already linked."))
agreement = self.env['purchase.requisition'].create({ agreement = self.env['purchase.requisition'].create({
@ -406,6 +404,9 @@ class AnnualPurchaseRequest(models.Model):
rec.message_post(body=_("Purchase Agreement created and linked.")) rec.message_post(body=_("Purchase Agreement created and linked."))
self.write({'state':'approved'}) self.write({'state':'approved'})
class AnnualPurchaseRequestLine(models.Model): class AnnualPurchaseRequestLine(models.Model):
_name = "odx.annual.request.line" _name = "odx.annual.request.line"
_description = "Annual Purchase Request Line" _description = "Annual Purchase Request Line"

View File

@ -15,7 +15,6 @@ class PurchaseRFQ(models.Model):
vendor_id = fields.Many2one( vendor_id = fields.Many2one(
'res.partner', 'res.partner',
string='Vendor', string='Vendor',
domain=[('supplier_rank', '>', 0)]
) )
partner_id = fields.Many2one( partner_id = fields.Many2one(
'res.partner', 'res.partner',
@ -120,20 +119,37 @@ class PurchaseRFQ(models.Model):
def action_refuse_rfq(self): def action_refuse_rfq(self):
self.ensure_one() self.ensure_one()
member = self._get_current_member_vote() member = self.committe_members.filtered(lambda m: m.user_id == self.env.user)
if member and member.refused: if member and member.refused:
raise UserError(_("You have already refused this RFQ before.")) raise UserError(_("You have already refused this RFQ before."))
return {
'type': 'ir.actions.act_window', else:
'name': _('Refuse Reason'), self.env['committe.member'].create({
'res_model': 'refuse.reason.rfq', 'rfq_id': self.id,
'view_mode': 'form', 'user_id': self.env.user.id,
'target': 'new', 'refused': True,
'context': { 'select': False
'default_rfq_id': self.id })
}
} self._check_committee_rejection()
def _check_committee_rejection(self):
self.ensure_one()
if not self.source_request_ref or not self.source_request_ref.committe_members:
return
requisition_users = self.source_request_ref.committe_members
rfq_users = self.committe_members.mapped('user_id')
if set(requisition_users.ids) == set(rfq_users.ids):
if self.committe_members and all(member.refused for member in self.committe_members):
self.source_request_ref.write({'state': 'rejected_by_committee'})
self.action_reject()
self.message_post(body=_("تم رفض عرض السعر من قبل جميع أعضاء اللجنة."))
def action_submit_to_committees(self): def action_submit_to_committees(self):
res = super(PurchaseRFQ, self).action_submit_to_committees() res = super(PurchaseRFQ, self).action_submit_to_committees()
@ -168,6 +184,19 @@ class PurchaseRFQ(models.Model):
'target': 'current', 'target': 'current',
} }
def write(self, vals):
res = super().write(vals)
if 'state' in vals:
for rec in self:
if rec.state in ['po', 'approved']:
related_rfqs = self.search([
('source_request_ref', '=', rec.source_request_ref.id),
('id', '!=', rec.id),
('state', 'not in', ['po', 'approved', 'rejected'])
])
related_rfqs.write({'state': 'rejected'})
return res
def action_rfq_send(self): def action_rfq_send(self):
self.ensure_one() self.ensure_one()
if not self.vendor_id: if not self.vendor_id:
@ -176,7 +205,7 @@ class PurchaseRFQ(models.Model):
self.partner_id = self.vendor_id.id self.partner_id = self.vendor_id.id
try: try:
template = self.env.ref('odex25_annual_purchase.email_template_annual_rfq') template = self.env.ref('odex25_annual_purchase.email_template_annual_rfq_ar')
except ValueError: except ValueError:
template = False template = False
@ -209,7 +238,6 @@ class PurchaseRFQ(models.Model):
ctx['model_description'] = _('Request for Quotation') ctx['model_description'] = _('Request for Quotation')
self.state ='sent'
return { return {
'name': _('Compose Email'), 'name': _('Compose Email'),
'type': 'ir.actions.act_window', 'type': 'ir.actions.act_window',
@ -233,8 +261,8 @@ class PurchaseRFQ(models.Model):
if any((l.quantity or 0.0) <= 0.0 for l in self.line_ids): if any((l.quantity or 0.0) <= 0.0 for l in self.line_ids):
raise UserError(_('ضروري الكمية تكون أكبر من 0.')) raise UserError(_('ضروري الكمية تكون أكبر من 0.'))
if any((l.price_unit or 0.0) <= 0.0 for l in self.line_ids): # if any((l.price_unit or 0.0) <= 0.0 for l in self.line_ids):
raise UserError(_('رجاء أضِف الأسعار لكل السطور.')) # raise UserError(_('رجاء أضِف الأسعار لكل السطور.'))
def _sync_approved_prices_to_request(self): def _sync_approved_prices_to_request(self):
@ -318,6 +346,18 @@ class PurchaseRFQ(models.Model):
rec.state = 'rejected' rec.state = 'rejected'
class MailComposeMessage(models.TransientModel):
_inherit = 'mail.compose.message'
def send_mail(self, auto_commit=False):
res = super(MailComposeMessage, self).send_mail(auto_commit=auto_commit)
if self.model == 'annual.rfq' and self.res_id:
rfq = self.env['annual.rfq'].browse(self.res_id)
rfq.write({'state': 'sent'})
return res
class PurchaseRFQLine(models.Model): class PurchaseRFQLine(models.Model):
_name = 'annual.rfq.line' _name = 'annual.rfq.line'
_description = 'RFQ Line Items' _description = 'RFQ Line Items'

View File

@ -1,6 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_request_staff,access_request_staff,model_odx_annual_request,purchase.group_purchase_user,1,1,1,0 access_request_staff,access_request_staff,model_odx_annual_request,purchase.group_purchase_user,1,1,1,0
access_request_manager,access_request_manager,model_odx_annual_request,purchase.group_purchase_manager,1,1,1,0 access_request_manager,access_request_manager,model_odx_annual_request,purchase.group_purchase_manager,1,1,0,0
access_request_committee,access_request_committee,model_odx_annual_request,purchase_requisition_custom.committe_member,1,0,0,0 access_request_committee,access_request_committee,model_odx_annual_request,purchase_requisition_custom.committe_member,1,0,0,0
access_request_ssd,access_request_ssd,model_odx_annual_request,hr_base.group_services_manager,1,1,0,0 access_request_ssd,access_request_ssd,model_odx_annual_request,hr_base.group_services_manager,1,1,0,0
access_request_gm,access_request_gm,model_odx_annual_request,hr_base.group_general_manager,1,1,0,0 access_request_gm,access_request_gm,model_odx_annual_request,hr_base.group_general_manager,1,1,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_request_staff access_request_staff model_odx_annual_request purchase.group_purchase_user 1 1 1 0
3 access_request_manager access_request_manager model_odx_annual_request purchase.group_purchase_manager 1 1 1 0 0
4 access_request_committee access_request_committee model_odx_annual_request purchase_requisition_custom.committe_member 1 0 0 0
5 access_request_ssd access_request_ssd model_odx_annual_request hr_base.group_services_manager 1 1 0 0
6 access_request_gm access_request_gm model_odx_annual_request hr_base.group_general_manager 1 1 0 0

View File

@ -23,6 +23,7 @@
<form string="Annual Purchase Request"> <form string="Annual Purchase Request">
<header> <header>
<field name="can_create_agreement" invisible="1"/> <field name="can_create_agreement" invisible="1"/>
<button name="action_send" string="Send" type="object" states="draft" class="btn-primary" groups="purchase.group_purchase_user" icon="fa-send"/> <button name="action_send" string="Send" type="object" states="draft" class="btn-primary" groups="purchase.group_purchase_user" icon="fa-send"/>
<button name="action_manager_approve" string="Approve" type="object" class="btn-primary" states="to_manager" groups="purchase.group_purchase_manager" icon="fa-check-circle"/> <button name="action_manager_approve" string="Approve" type="object" class="btn-primary" states="to_manager" groups="purchase.group_purchase_manager" icon="fa-check-circle"/>
<button name="action_manager_reject" string="Reject" type="object" states="to_manager" groups="purchase.group_purchase_manager" class="btn-danger" icon="fa-times-circle"/> <button name="action_manager_reject" string="Reject" type="object" states="to_manager" groups="purchase.group_purchase_manager" class="btn-danger" icon="fa-times-circle"/>
@ -35,9 +36,10 @@
<button name="action_ceo_reject" string="Reject" type="object" states="ceo" groups="hr_base.group_general_manager" class="btn-danger" icon="fa-times-circle"/> <button name="action_ceo_reject" string="Reject" type="object" states="ceo" groups="hr_base.group_general_manager" class="btn-danger" icon="fa-times-circle"/>
<button name="action_create_agreement" string="Create Agreement" type="object" states="purchase" class="btn-primary" groups="purchase.group_purchase_manager" icon="fa-file-contract"/> <button name="action_create_agreement" string="Create Agreement" type="object" states="purchase" class="btn-primary" groups="purchase.group_purchase_manager" icon="fa-file-contract"/>
<button name="action_to_draft" string="Back To Draft" type="object" states="rejected" class="btn-secondary" groups="odex25_annual_purchase.group_annual_to_draft" icon="fa-undo" /> <button name="action_to_draft" string="Back To Draft" type="object" states="rejected" class="btn-secondary" groups="odex25_annual_purchase.group_annual_to_draft" icon="fa-undo" />
<field name="state" widget="statusbar" statusbar_visible="draft,to_manager,procurement,committee,ssd,ceo,approved,rejected,cancel"/> <field name="state" widget="statusbar" statusbar_visible="draft,to_manager,procurement,committee,ssd,ceo,approved,rejected,cancel"/>
</header> </header>
<sheet> <sheet attrs="{'readonly': [('state', '!=', 'draft')]}">
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="object" name="action_open_rfqs" icon="fa-list-alt"> <button class="oe_stat_button" type="object" name="action_open_rfqs" icon="fa-list-alt">
<field name="rfq_count" widget="statinfo" string="RFQs"/> <field name="rfq_count" widget="statinfo" string="RFQs"/>
@ -71,35 +73,35 @@
<group> <group>
<field name="sent_to_commitee" invisible="1"/> <field name="sent_to_commitee" invisible="1"/>
<field name="department_id"/> <field name="department_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="product_category_ids" widget="many2many_tags"/> <field name="product_category_ids" widget="many2many_tags" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="date"/> <field name="date" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="ssd_approve"/> <field name="ssd_approve" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="seo_approve"/> <field name="seo_approve" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
</group> </group>
<group> <group attrs="{'readonly': [('state', '!=', 'draft')]}">
<field name="employee_id" readonly="1"/> <field name="employee_id" readonly="1"/>
<field name="user_id" invisible="1"/> <field name="user_id" invisible="1"/>
<field name="purpose"/> <field name="purpose" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="date_start" invisible="1"/> <field name="date_start" invisible="1"/>
<field name="date_end" invisible="1"/> <field name="date_end" invisible="1"/>
<field name="vendor_id" invisible="1"/> <field name="vendor_id" invisible="1"/>
<field name="committee_enabled"/> <field name="committee_enabled" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="committee_type_id" <field name="committee_type_id"
attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)]}"/> attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)],'readonly': [('state', '!=', 'draft')] }"/>
<field name="committe_head" <field name="committe_head"
attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)]}"/> attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)],'readonly': [('state', '!=', 'draft')] }"/>
<field name="agreement_id" invisible="1"/> <field name="agreement_id" invisible="1"/>
<field name="min_approve" <field name="min_approve"
attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)]}"/> attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)],'readonly': [('state', '!=', 'draft')] }"/>
<field name="min_vote" <field name="min_vote"
attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)]}"/> attrs="{'invisible': [('committee_enabled', '=', False)], 'required': [('committee_enabled', '=', True)],'readonly': [('state', '!=', 'draft')] }"/>
</group> </group>
@ -107,7 +109,7 @@
</group> </group>
<notebook> <notebook>
<page string="Products"> <page string="Products">
<field name="line_ids"> <field name="line_ids" attrs="{'readonly': [('state', '!=', 'draft')]}">
<tree editable="bottom" delete="true"> <tree editable="bottom" delete="true">
<field name="product_id"/> <field name="product_id"/>
<field name="description"/> <field name="description"/>
@ -121,7 +123,7 @@
<page string="Description"> <page string="Description">
<group> <group>
<field name="description"/> <field name="description" attrs="{'readonly': [('state', '!=', 'draft')]}" />
</group> </group>
</page> </page>
@ -129,7 +131,7 @@
<page string="Committee " attrs="{'invisible': [('committee_enabled', '=', False)]}"> <page string="Committee " attrs="{'invisible': [('committee_enabled', '=', False)]}">
<group> <group>
<field name="committe_members" <field name="committe_members"
attrs="{'required': [('committee_enabled', '=', True)]}"> attrs="{'required': [('committee_enabled', '=', True)],'readonly': [('state', '!=', 'draft')] }">
<tree> <tree>
<field name="name"/> <field name="name"/>
</tree> </tree>

View File

@ -25,6 +25,8 @@
class="btn-primary" class="btn-primary"
attrs="{'invisible': [('state', 'not in', ['sent','draft'])]}" attrs="{'invisible': [('state', 'not in', ['sent','draft'])]}"
/> />
<button name="action_rfq_send" states="sent" string="Re-Send by Email" type="object" context="{'send_rfq':True}"/>
<button name="action_select_rfq" <button name="action_select_rfq"
type="object" type="object"
@ -60,7 +62,7 @@
</button> </button>
</div> </div>
<div class="oe_title"> <div class="oe_title">
<span class="o_form_label" >Request for Quotation</span> <span class="o_form_label" >طلب عرض سعر</span>
<h1> <h1>
<!-- <field name="priority" widget="priority" class="mr-3"/>--> <!-- <field name="priority" widget="priority" class="mr-3"/>-->
<field name="name" readonly="1"/> <field name="name" readonly="1"/>
@ -111,7 +113,7 @@
<div class="oe_clear"/> <div class="oe_clear"/>
</page> </page>
<page id="page_committee_members" string="Committe Members" <page id="page_committee_members" string="Committe Members"
groups="odex25_annual_purchase.group_technical_committee,odex25_annual_purchase.group_annual_committee" groups="odex25_annual_purchase.group_technical_committee,odex25_annual_purchase.group_annual_committee,purchase.group_purchase_user"
> >
<field name="committe_members"> <field name="committe_members">
<tree> <tree>
@ -186,7 +188,7 @@
<field name="recommendation_order" /> <field name="recommendation_order" />
<field name="vendor_id"/> <field name="vendor_id"/>
<field name="request_date"/> <field name="request_date"/>
<field name="amount_total"/> <field name="amount_total" groups="hr_base.group_general_manager,hr_base.group_services_manager,purchase.group_purchase_user,purchase.group_purchase_manager,odex25_annual_purchase.group_annual_committee"/>
<field name="state"/> <field name="state"/>
</tree> </tree>
</field> </field>