Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][ADD] purchase_duplicate_check: Module added #2516

Open
wants to merge 6 commits into
base: 16.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions purchase_duplicate_check/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
========================
Purchase Duplicate Check
========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:7107035505d1a2624df4c1bce4fc742fd17319625c9a8ea54b4558c8e7c35107
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpurchase--workflow-lightgray.png?logo=github
:target: https://github.com/OCA/purchase-workflow/tree/16.0/purchase_duplicate_check
:alt: OCA/purchase-workflow
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/purchase-workflow-16-0/purchase-workflow-16-0-purchase_duplicate_check
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/purchase-workflow&target_branch=16.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module adds the following features to the Purchase App:

Pending RFQ or Purchase orders with associated incoming
stock picking not in the "Done" state for Product in PO Line:

- RFQ Confirmation Wizard if there's existing RFQ for the same products
- Add "Pending orders" field to the PO Line
- Assign Acitivty for the responsible user to check the associated orders

**Table of contents**

.. contents::
:local:

Use Cases / Context
===================

This module helps prevent overordering of the same products by streamlining the control of Requests for Quotations (RFQs) and undelivered Purchase Orders (POs).

Configuration
=============

To Configure the Activity for a responsible person to check the repeating RFQ/PO:

Go to Settings -> Purchase - > Orders

- Enable the "Create Activity for Repeating Orders" Checkbox
- Select the activity type in the "Activity" Field
- Press the "Arrow button" to Configure the Default user for activity

Usage
=====

Pending RFQ for Product in PO Line:

Go to the Purchase App:

- Create a new order
- Press the "Add a Line" button to add purchase line
- Select a Product for the PO Line
- If there are pending RFQs for the same product in the "Draft"/"Sent" state, or Purchase orders with associated incoming stock picking not in the "Done" state it will be shown in the "Pending order" field
- Press the "Confirm Order" button to confirm the RFQ
- If there's a Pending RFQ's for the products in PO Lines, the Confirmation wizard with Pending RFQ's information will appear
- Press the "Confirm" button to process with the order or "Cancel" to get back to RFQ

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/purchase-workflow/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/purchase-workflow/issues/new?body=module:%20purchase_duplicate_check%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* Cetmix

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/purchase-workflow <https://github.com/OCA/purchase-workflow/tree/16.0/purchase_duplicate_check>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions purchase_duplicate_check/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizard
21 changes: 21 additions & 0 deletions purchase_duplicate_check/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "Purchase Duplicate Check",
"version": "16.0.1.0.0",
"summary": "Prevents overordering in the Purchase app with a Confirmation Wizard, "
"'Pending Orders' field, and activity tracking for repeated orders.",
"author": "Cetmix, Odoo Community Association (OCA)",
"license": "AGPL-3",
"category": "Inventory/Purchase",
"website": "https://github.com/OCA/purchase-workflow",
"live_test_url": "https://demo.cetmix.com",
geomer198 marked this conversation as resolved.
Show resolved Hide resolved
"depends": ["purchase_stock", "confirmation_wizard"],
"external_dependencies": {},
geomer198 marked this conversation as resolved.
Show resolved Hide resolved
"data": [
"views/purchase_order_views.xml",
"views/res_config_settings_views.xml",
"wizard/confirmation_wizard_views.xml",
],
"assets": {},
geomer198 marked this conversation as resolved.
Show resolved Hide resolved
"installable": True,
"application": False,
}
2 changes: 2 additions & 0 deletions purchase_duplicate_check/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import purchase_order
from . import res_config_settings
113 changes: 113 additions & 0 deletions purchase_duplicate_check/models/purchase_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from odoo import _, fields, models


class PurchaseOrder(models.Model):
_inherit = "purchase.order"

def _prepare_pending_orders_message(self, product_id):
"""
Prepare pending order line message

:param product_id: product.product record id
:return str: message
"""
message = ""
for order in self:
product_line = order.order_line.filtered(
lambda line: line.product_id.id == product_id
)
if not product_line:
continue

product_qty = sum(product_line.mapped("product_qty"))
order_date = order.create_date.date()
order_href = (
f"<a href='/web#id={order.id}&model={order._name}'>{order.name}</a>"
)
type_ = order.state in ["draft", "sent"] and "RFQ" or "PO"
message += (
f"{type_}: {order_href} date: {order_date} Qty: {product_qty}<br/>"
)
return message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _prepare_pending_orders_message(self, product_id):
"""
Prepare pending order line message
:param product_id: product.product record id
:return str: message
"""
message = ""
for order in self:
product_line = order.order_line.filtered(
lambda line: line.product_id.id == product_id
)
if not product_line:
continue
product_qty = sum(product_line.mapped("product_qty"))
order_date = order.create_date.date()
order_href = (
f"<a href='/web#id={order.id}&model={order._name}'>{order.name}</a>"
)
type_ = order.state in ["draft", "sent"] and "RFQ" or "PO"
message += (
f"{type_}: {order_href} date: {order_date} Qty: {product_qty}<br/>"
)
return message
def _prepare_pending_orders_message(self, product_id):
"""
Prepare pending order line message
:param product_id: product.product record id
:return str: message
"""
self.ensure_one()
order_lines = self.env["purchase.order.line"].search([
('product_id', '=', product_id),
('order_id', 'in', self.ids)
])
message_parts = []
for line in order_lines:
order = line.order_id
order_href = f"<a href='/web#id={order.id}&model={order._name}'>{order.name}</a>"
type_ = "RFQ" if order.state in ["draft", "sent"] else "PO"
message_parts.append(
f"{type_}: {order_href} date: {order.create_date.date()} Qty: {line.product_qty}<br/>"
)
return "".join(message_parts)


def _check_pending_order(self):
if (
not self.env["ir.config_parameter"]
.sudo()
.get_param(
"purchase_duplicate_check.create_activity_repeating_orders", False
)
):
return
if not self._context.get("skip_rfq_confirmation"):
return (
self.env["confirmation.wizard"]
.with_context(skip_rfq_confirmation=True)
.confirm_pending_order(self)
)
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, create separate method to check settings, e.g.:

def _is_activity_enabled(self):
    """
    Check if activity for repeating orders is enabled.
    :return: bool
    """
    return self.env["ir.config_parameter"].sudo().get_param(
        "purchase_duplicate_check.create_activity_repeating_orders", False
    )

and

def _check_pending_order(self):
    """
    Check for pending orders and trigger confirmation wizard if needed.
    """
    if self._is_activity_enabled() and not self._context.get("skip_rfq_confirmation"):
        return self.env["confirmation.wizard"].with_context(
            skip_rfq_confirmation=True
        ).confirm_pending_order(self)


def button_confirm(self):
action = self._check_pending_order()
if action is not None:
return action
return super().button_confirm()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def button_confirm(self):
    """
    Confirm the purchase order.
    :return: action or super
    """
    action = self._check_pending_order()
    return action or super().button_confirm()



class PurchaseOrderLine(models.Model):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, create separate file for this model: purchase_order_line.py

_inherit = "purchase.order.line"

pending_order_ids = fields.Many2many(
"purchase.order",
string="Pending Orders",
compute="_compute_pending_order_ids",
)

def _compute_pending_order_ids(self):
purchase_order_obj = self.env["purchase.order"]
for rec in self:
if rec.product_type != "product":
rec.pending_order_ids = False
continue
rfq_orders = purchase_order_obj.search(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is necessary to use the read_group method instead of searching for records in a loop, which will reduce the load and optimize the query into the database.

[
("order_line.product_id", "=", rec.product_id.id),
("id", "!=", rec.order_id._origin.id),
"|",
("state", "in", ["draft", "sent"]),
"&",
"&",
("state", "not in", ["draft", "sent"]),
("picking_ids.picking_type_id.code", "=", "incoming"),
("picking_ids.state", "not in", ["done", "cancel"]),
]
)
rec.pending_order_ids = rfq_orders

def _get_order_confirm_message(self):
"""Get order confirmation message for pending orders"""
message = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message_parts = []

for line in self:
pending_orders = line.pending_order_ids
if not pending_orders:
continue

Check warning on line 93 in purchase_duplicate_check/models/purchase_order.py

View check run for this annotation

Codecov / codecov/patch

purchase_duplicate_check/models/purchase_order.py#L93

Added line #L93 was not covered by tests
product_line_msg = pending_orders._prepare_pending_orders_message(
line.product_id.id
)
message += f"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

message_parts.append(

Product <b>{line.product_id.name}</b><br/>
{product_line_msg}<br/>
"""
return message
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"".join(message_parts)


def action_open_pending_orders(self):
"""Action open pending purchase orders"""
self.ensure_one()
return {

Check warning on line 106 in purchase_duplicate_check/models/purchase_order.py

View check run for this annotation

Codecov / codecov/patch

purchase_duplicate_check/models/purchase_order.py#L105-L106

Added lines #L105 - L106 were not covered by tests
"name": _("Pending Orders"),
"views": [[False, "tree"], [False, "form"]],
"res_model": "purchase.order",
"type": "ir.actions.act_window",
"domain": [("id", "in", self.pending_order_ids.ids)],
"context": {"create": False},
}
19 changes: 19 additions & 0 deletions purchase_duplicate_check/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from odoo import api, fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

create_activity_repeating_orders = fields.Boolean(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please, rename create_activity_repeating_orders -> allow_create_activity_repeating_orders

config_parameter="purchase_duplicate_check.create_activity_repeating_orders"
)
repeating_orders_activity_type_id = fields.Many2one(
comodel_name="mail.activity.type",
config_parameter="purchase_duplicate_check.repeating_orders_activity_type_id",
string="Activity",
)

@api.onchange("create_activity_repeating_orders")
def _onchange_create_activity_repeating_orders(self):
if not self.create_activity_repeating_orders:
self.repeating_orders_activity_type_id = False

Check warning on line 19 in purchase_duplicate_check/models/res_config_settings.py

View check run for this annotation

Codecov / codecov/patch

purchase_duplicate_check/models/res_config_settings.py#L19

Added line #L19 was not covered by tests
7 changes: 7 additions & 0 deletions purchase_duplicate_check/readme/CONFIGURE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
To Configure the Activity for a responsible person to check the repeating RFQ/PO:

Go to Settings -> Purchase - > Orders

- Enable the "Create Activity for Repeating Orders" Checkbox
- Select the activity type in the "Activity" Field
- Press the "Arrow button" to Configure the Default user for activity
1 change: 1 addition & 0 deletions purchase_duplicate_check/readme/CONTEXT.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This module helps prevent overordering of the same products by streamlining the control of Requests for Quotations (RFQs) and undelivered Purchase Orders (POs).
8 changes: 8 additions & 0 deletions purchase_duplicate_check/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
This module adds the following features to the Purchase App:

Pending RFQ or Purchase orders with associated incoming
stock picking not in the "Done" state for Product in PO Line:

- RFQ Confirmation Wizard if there's existing RFQ for the same products
- Add "Pending orders" field to the PO Line
- Assign Acitivty for the responsible user to check the associated orders
11 changes: 11 additions & 0 deletions purchase_duplicate_check/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Pending RFQ for Product in PO Line:

Go to the Purchase App:

- Create a new order
- Press the "Add a Line" button to add purchase line
- Select a Product for the PO Line
- If there are pending RFQs for the same product in the "Draft"/"Sent" state, or Purchase orders with associated incoming stock picking not in the "Done" state it will be shown in the "Pending order" field
- Press the "Confirm Order" button to confirm the RFQ
- If there's a Pending RFQ's for the products in PO Lines, the Confirmation wizard with Pending RFQ's information will appear
- Press the "Confirm" button to process with the order or "Cancel" to get back to RFQ
Loading
Loading