diff --git a/purchase_rfq_number/__init__.py b/purchase_rfq_number/__init__.py new file mode 100644 index 00000000000..31660d6a965 --- /dev/null +++ b/purchase_rfq_number/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/purchase_rfq_number/__manifest__.py b/purchase_rfq_number/__manifest__.py new file mode 100644 index 00000000000..49aa56d01fd --- /dev/null +++ b/purchase_rfq_number/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2021 ProThai Technology Co.,Ltd. (http://prothaitechnology.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Purchase For Quotation Numeration", + "summary": "Different sequence for purchase for quotations", + "version": "14.0.1.0.0", + "author": "ProThai, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Purchase Management", + "depends": ["purchase"], + "website": "https://github.com/OCA/purchase-workflow", + "data": [ + "data/ir_sequence_data.xml", + "reports/purchase_report_templates.xml", + "views/res_config_settings_views.xml", + "views/purchase_views.xml", + ], + "maintainer": ["prapassornS"], + "installable": True, + "auto_install": False, +} diff --git a/purchase_rfq_number/data/ir_sequence_data.xml b/purchase_rfq_number/data/ir_sequence_data.xml new file mode 100644 index 00000000000..13e371d4430 --- /dev/null +++ b/purchase_rfq_number/data/ir_sequence_data.xml @@ -0,0 +1,11 @@ + + + + + Requests for Quotation + purchase.rfq + RFQ + 3 + + + diff --git a/purchase_rfq_number/models/__init__.py b/purchase_rfq_number/models/__init__.py new file mode 100644 index 00000000000..fb6d4c498ef --- /dev/null +++ b/purchase_rfq_number/models/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import res_company +from . import purchase_order diff --git a/purchase_rfq_number/models/purchase_order.py b/purchase_rfq_number/models/purchase_order.py new file mode 100644 index 00000000000..d2036864655 --- /dev/null +++ b/purchase_rfq_number/models/purchase_order.py @@ -0,0 +1,67 @@ +# Copyright 2021 ProThai Technology Co.,Ltd. (http://prothaitechnology.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 + +from odoo import api, fields, models + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + rfq_number = fields.Char( + string="RFQ Reference", + index=True, + copy=False, + default="New", + ) + + @api.model + def create(self, vals): + + if "company_id" in vals: + keep_name_po = ( + self.env["res.company"].browse(vals.get("company_id")).keep_name_po + ) + else: + keep_name_po = self.env.company.keep_name_po + + if not keep_name_po and vals.get("name", "New") == "New": + vals["name"] = self.env["ir.sequence"].next_by_code("purchase.rfq") or "New" + + return super().create(vals) + + def button_confirm(self): + for order in self: + if order.state in ["draft", "sent"] and not order.company_id.keep_name_po: + if order.company_id.auto_attachment_rfq: + # save rfq pdf as attachment + order.action_get_rfq_attachment() + + order.write( + { + "rfq_number": order.name, + "name": self.env["ir.sequence"].next_by_code("purchase.order"), + } + ) + + return super().button_confirm() + + def action_get_rfq_attachment(self): + rfq_pdf = self.env.ref("purchase.report_purchase_quotation")._render_qweb_pdf( + self.id + )[0] + return self.env["ir.attachment"].create( + { + "name": "{}.pdf".format(self.name), + "type": "binary", + "datas": base64.encodebytes(rfq_pdf), + "res_model": self._name, + "res_id": self.id, + } + ) + + def button_draft(self): + for rec in self.filtered(lambda l: l.rfq_number != "New"): + rec.name = rec.rfq_number + return super().button_draft() diff --git a/purchase_rfq_number/models/res_company.py b/purchase_rfq_number/models/res_company.py new file mode 100644 index 00000000000..b4e8939b9af --- /dev/null +++ b/purchase_rfq_number/models/res_company.py @@ -0,0 +1,29 @@ +# Copyright 2021 ProThai Technology Co.,Ltd. (http://prothaitechnology.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + keep_name_po = fields.Boolean( + string="Use Same Enumeration", + help="If this is unchecked, purchase rfq use a different sequence from " + "Purchase orders", + default=True, + ) + auto_attachment_rfq = fields.Boolean( + string="Auto Attachment RFQ", + help="Auto attachment requests for quotation after confirm", + default=False, + ) + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + keep_name_po = fields.Boolean(related="company_id.keep_name_po", readonly=False) + auto_attachment_rfq = fields.Boolean( + related="company_id.auto_attachment_rfq", readonly=False + ) diff --git a/purchase_rfq_number/readme/CONFIGURE.rst b/purchase_rfq_number/readme/CONFIGURE.rst new file mode 100644 index 00000000000..c460610be02 --- /dev/null +++ b/purchase_rfq_number/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +To configure this module, you need to: + +* Go to **Purchases -> Configuration and uncheck 'Use same enumeration for purchase rfq and purchase orders'.** +* Go to **Purchases -> Configuration and check 'Auto attachment requests for quotation after confirm'.** diff --git a/purchase_rfq_number/readme/CONTRIBUTORS.rst b/purchase_rfq_number/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..1802e4ddfe5 --- /dev/null +++ b/purchase_rfq_number/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Prapassorn Sornkaew diff --git a/purchase_rfq_number/readme/CREDITS.rst b/purchase_rfq_number/readme/CREDITS.rst new file mode 100644 index 00000000000..cc056a80d6d --- /dev/null +++ b/purchase_rfq_number/readme/CREDITS.rst @@ -0,0 +1 @@ +* Odoo Community Association: `Icon `_. diff --git a/purchase_rfq_number/readme/DESCRIPTION.rst b/purchase_rfq_number/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..e9293f945aa --- /dev/null +++ b/purchase_rfq_number/readme/DESCRIPTION.rst @@ -0,0 +1,32 @@ +Note: This module is similar to sale_quotation_number, but for purchase/rfq + +* Purchase for Quotation: + + * Purchase process in draft stage just informing prices and element of communication. + +* Purchase Order: + + * Purchase process confirmed, the customer already have a compromise with us in terms of pay an invoice and receive our product or service. + +**Technical Explanation** + +When you create a purchase quotation, it is numbered using the 'purchase.rfq' +sequence. When you confirm a purchase quotation, its rfq number is saved in the +'rfq_number' field and the purchase order gets a new number, retrieving it from +'purchase.order' sequence. + +* With Odoo Base: + + Purchase for Quotation 1 Number = PO001 + + Purchase for Quotation 2 Number = PO002 + +* With Odoo + This Module: + + Purchase for Quotation 1 Number = RFQ001 + + Purchase for Quotation 2 Number = RFQ002 + + Purchase for Quotation 1 Confirmed = Number for Purchase Order PO001 from Purchase for Quotation RFQ001 + + Purchase for Quotation 2 Confirmed = Number for Purchase Order PO002 from Purchase for Quotation RFQ002 diff --git a/purchase_rfq_number/reports/purchase_report_templates.xml b/purchase_rfq_number/reports/purchase_report_templates.xml new file mode 100644 index 00000000000..30f880738b4 --- /dev/null +++ b/purchase_rfq_number/reports/purchase_report_templates.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + diff --git a/purchase_rfq_number/static/description/icon.png b/purchase_rfq_number/static/description/icon.png new file mode 100755 index 00000000000..3a0328b516c Binary files /dev/null and b/purchase_rfq_number/static/description/icon.png differ diff --git a/purchase_rfq_number/tests/__init__.py b/purchase_rfq_number/tests/__init__.py new file mode 100644 index 00000000000..08ce35e2681 --- /dev/null +++ b/purchase_rfq_number/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_purchase_order diff --git a/purchase_rfq_number/tests/test_purchase_order.py b/purchase_rfq_number/tests/test_purchase_order.py new file mode 100644 index 00000000000..c560768f80e --- /dev/null +++ b/purchase_rfq_number/tests/test_purchase_order.py @@ -0,0 +1,91 @@ +# Copyright 2021 ProThai Technology Co.,Ltd. (http://prothaitechnology.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase + + +class TestPurchaseOrder(TransactionCase): + def setUp(self, *args, **kwargs): + super(TestPurchaseOrder, self).setUp() + self.purchase_order_model = self.env["purchase.order"] + company = self.env.company + company.keep_name_po = False + company.auto_attachment_rfq = True + + def test_enumeration(self): + order1 = self.purchase_order_model.create( + {"partner_id": self.env.ref("base.res_partner_1").id} + ) + + purchase_for_quotation1_name = order1.name + order2 = self.purchase_order_model.create( + {"partner_id": self.env.ref("base.res_partner_1").id} + ) + purchase_for_quotation2_name = order2.name + + self.assertRegex(purchase_for_quotation1_name, "RFQ") + self.assertRegex(purchase_for_quotation2_name, "RFQ") + self.assertLess( + int(purchase_for_quotation1_name[4:]), int(purchase_for_quotation2_name[4:]) + ) + + order2.button_confirm() + order1.button_confirm() + + self.assertRegex(order1.name, "P") + self.assertEqual(order1.rfq_number, purchase_for_quotation1_name) + + self.assertRegex(order2.name, "P") + self.assertEqual(order2.rfq_number, purchase_for_quotation2_name) + self.assertLess(int(order2.name[3:]), int(order1.name[3:])) + + def test_with_rfq_number(self): + rfq_number = "rfq_number" + order1 = self.purchase_order_model.create( + { + "rfq_number": rfq_number, + "partner_id": self.env.ref("base.res_partner_1").id, + } + ) + purchase_for_quotation1_name = order1.name + order1.button_confirm() + + self.assertRegex(order1.name, "P") + self.assertEqual(order1.rfq_number, purchase_for_quotation1_name) + + def test_error_confirmation_sequence(self): + order = self.purchase_order_model.create( + { + "partner_id": self.env.ref("base.res_partner_1").id, + "state": "done", + } + ) + sequence_id = self.env["ir.sequence"].search( + [ + ("code", "=", "purchase.order"), + ("company_id", "in", [order.company_id.id, False]), + ] + ) + next_name = sequence_id.get_next_char(sequence_id.number_next_actual) + try: + order.button_confirm() + except UserError: + pass + order.update({"state": "draft"}) + # Now the RFQ can be confirmed + order.button_confirm() + self.assertEqual(next_name, order.name) + + def test_auto_attachment_rfq(self): + order = self.purchase_order_model.create( + { + "partner_id": self.env.ref("base.res_partner_1").id, + "state": "draft", + } + ) + order.button_confirm() + attachment = self.env["ir.attachment"].search( + [("res_model", "=", "purchase.order"), ("res_id", "=", order.id)] + ) + self.assertEqual(attachment.res_id, order.id) diff --git a/purchase_rfq_number/views/purchase_views.xml b/purchase_rfq_number/views/purchase_views.xml new file mode 100644 index 00000000000..58822979de4 --- /dev/null +++ b/purchase_rfq_number/views/purchase_views.xml @@ -0,0 +1,15 @@ + + + purchase.order.form + purchase.order + + + + + + + + diff --git a/purchase_rfq_number/views/res_config_settings_views.xml b/purchase_rfq_number/views/res_config_settings_views.xml new file mode 100644 index 00000000000..7a5b176a177 --- /dev/null +++ b/purchase_rfq_number/views/res_config_settings_views.xml @@ -0,0 +1,42 @@ + + + + + Purchase RFQ number configuration + res.config.settings + + + + + + + + + + + If this is unchecked, purchase rfq use a different sequence from purchase orders + + + + + + + + + + + Auto attachment requests for quotation after confirm + + + + + + + diff --git a/setup/purchase_rfq_number/odoo/addons/purchase_rfq_number b/setup/purchase_rfq_number/odoo/addons/purchase_rfq_number new file mode 120000 index 00000000000..09d9638bbbf --- /dev/null +++ b/setup/purchase_rfq_number/odoo/addons/purchase_rfq_number @@ -0,0 +1 @@ +../../../../purchase_rfq_number \ No newline at end of file diff --git a/setup/purchase_rfq_number/setup.py b/setup/purchase_rfq_number/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/purchase_rfq_number/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)