forked from OCA/purchase-workflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ADD] purchase_by_packaging: Added module.
- Loading branch information
mibab.heli
committed
Mar 13, 2024
1 parent
3fc2e6e
commit e69fa60
Showing
22 changed files
with
1,153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
===================== | ||
Purchase By Packaging | ||
===================== | ||
|
||
.. | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! This file is generated by oca-gen-addon-readme !! | ||
!! changes will be overwritten. !! | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! source digest: sha256:53eb37fdcb59a67839d55db5839c08ddaa0eef12c42d2a09a26ef4d724404118 | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png | ||
:target: https://odoo-community.org/page/development-status | ||
:alt: Alpha | ||
.. |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_by_packaging | ||
: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_by_packaging | ||
: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 provides different configuration option to manage packagings on | ||
purchase orders. | ||
|
||
The creation/update of purchase order line will be blocked (by constraints) if the data | ||
on the purchase.order.line does not fit with the configuration of the product's | ||
packagings. | ||
|
||
It's also possible to force the quantity to purchase during creation/modification of | ||
the purchase order line if the "Force purchase quantity" is ticked on the packaging. | ||
|
||
For example, if your packaging is set to purchase by 5 units and the employee fill | ||
the quantity with 3, the quantity will be automatically replaced by 5 (it always | ||
rounds up). | ||
|
||
.. IMPORTANT:: | ||
This is an alpha version, the data model and design can change at any time without warning. | ||
Only for development or testing purpose, do not use in production. | ||
`More details on development status <https://odoo-community.org/page/development-status>`_ | ||
|
||
**Table of contents** | ||
|
||
.. contents:: | ||
:local: | ||
|
||
Configuration | ||
============= | ||
|
||
Following options are available to define which packaging type can be purchased and | ||
which product can only be purchased by packaging. | ||
|
||
* Can be purchased: On product packaging type model, this checkbox defines if product | ||
packagings from this particular type are available to be selected on purchase | ||
order line. | ||
|
||
* Purchase only by packaging: On product template model, this checkbox restricts | ||
purchases of these products if no packaging is selected on the purchase order line. | ||
If no packaging is selected, it will either be auto-assigned if the quantity | ||
on the purchase order line matches a packaging quantity or an error will be raised. | ||
|
||
* Force purchase quantity (on the packaging): force rounds up the quantity during | ||
creation/modification of the purchase order line with the factor set on the packaging. | ||
|
||
Known issues / Roadmap | ||
====================== | ||
|
||
* Odoo allows to define product.packaging records with a qty = 0. This does not | ||
make much sense with the can_be_purchased checkbox, and we probably need to add a | ||
constraint here. | ||
|
||
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_by_packaging%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 | ||
~~~~~~~ | ||
|
||
* Ametras | ||
|
||
Contributors | ||
~~~~~~~~~~~~ | ||
|
||
* Bastian Guenther <[email protected]> | ||
|
||
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_by_packaging>`_ project on GitHub. | ||
|
||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) | ||
{ | ||
"name": "Purchase By Packaging", | ||
"summary": "Manage purchase of packaging", | ||
"version": "16.0.1.0.0", | ||
"development_status": "Alpha", | ||
"category": "Warehouse Management", | ||
"website": "https://github.com/OCA/purchase-workflow", | ||
"author": "Ametras, Odoo Community Association (OCA)", | ||
"license": "AGPL-3", | ||
"application": False, | ||
"installable": True, | ||
"depends": [ | ||
"purchase_only_by_packaging", | ||
"product_packaging_level", | ||
"purchase_packaging_uom", | ||
], | ||
"data": [ | ||
"views/product_packaging.xml", | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from . import product_packaging | ||
from . import product_product | ||
from . import product_template | ||
from . import purchase_order_line |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Copyright 2021 Ametras | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) | ||
from odoo import api, fields, models | ||
|
||
|
||
class ProductPackaging(models.Model): | ||
_inherit = "product.packaging" | ||
|
||
purchase_rounding = fields.Float( | ||
string="Purchase Rounding Precision", | ||
digits="Product Unit of Measure", | ||
required=True, | ||
default=0.1, | ||
help="The allowed package quantity will be a multiple of this value. " | ||
"Use 1.0 for a package that cannot be further split.", | ||
) | ||
|
||
actual_purchase_qty = fields.Float(compute="_compute_actual_purchase_qty") | ||
|
||
@api.depends("purchase_rounding", "qty") | ||
def _compute_actual_purchase_qty(self): | ||
for record in self: | ||
record.actual_purchase_qty = record.purchase_rounding * record.qty |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Copyright 2020 Camptocamp SA | ||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
||
from odoo import fields, models | ||
from odoo.tools import float_compare, float_is_zero, float_round | ||
|
||
|
||
class ProductProduct(models.Model): | ||
_inherit = "product.product" | ||
|
||
def _convert_purchase_packaging_qty(self, qty, uom, packaging): | ||
""" | ||
Convert the given qty with given UoM to the packaging uom. | ||
To do that, first transform the qty to the reference UoM and then | ||
transform using the packaging UoM. | ||
The given qty is not updated if the product has purchase_only_by_packaging | ||
set to False or if the packaging is not set. | ||
Inspired from purchase_order_line_packaging_qty/ | ||
models.purchase_order_line.py _check_package(...) | ||
:param qty: float | ||
:return: float | ||
""" | ||
if not self or not packaging: | ||
return qty | ||
self.ensure_one() | ||
if packaging.force_purchase_qty: | ||
q = self.uom_po_id._compute_quantity(packaging.qty, uom) | ||
if ( | ||
qty | ||
and q | ||
and float_compare( | ||
qty / q, | ||
float_round(qty / q, precision_rounding=1.0), | ||
precision_rounding=0.001, | ||
) | ||
!= 0 | ||
): | ||
qty = qty - (qty % q) + q | ||
return qty | ||
|
||
def get_first_purchase_packaging_with_multiple_qty(self, qty): | ||
"""Return multiple of product packaging for one quantity if exist.""" | ||
self.ensure_one() | ||
packagings = self._get_purchase_packagings_with_multiple_qty(qty) | ||
return fields.first(packagings.sorted("qty")) | ||
|
||
def _get_purchase_packagings_with_multiple_qty(self, qty): | ||
self.ensure_one() | ||
return self.packaging_ids.filtered( | ||
lambda pack: pack.can_be_purchased | ||
and not float_is_zero( | ||
pack.qty, precision_rounding=pack.product_uom_po_id.rounding | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Copyright 2021 Ametras | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) | ||
from odoo import api, models | ||
|
||
|
||
class ProductTemplate(models.Model): | ||
_inherit = "product.template" | ||
|
||
@api.onchange("purchase_ok") | ||
def _change_purchase_ok(self): | ||
if not self.purchase_ok and self.purchase_only_by_packaging: | ||
self.purchase_only_by_packaging = False |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Copyright 2021 Ametras | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) | ||
from odoo import api, models | ||
|
||
|
||
class PurchaseOrderLine(models.Model): | ||
|
||
_inherit = "purchase.order.line" | ||
|
||
def get_packaging_qty(self, vals=None): | ||
if not vals: | ||
vals = [] | ||
product = ( | ||
self.env["product.product"].browse(vals["product_id"]) | ||
if "product_id" in vals | ||
else self.product_id | ||
) | ||
|
||
quantity = vals["product_qty"] if "product_qty" in vals else self.product_qty | ||
uom = ( | ||
self.env["uom.uom"].browse(vals["product_uom"]) | ||
if "product_uom" in vals | ||
else self.product_uom | ||
) | ||
packaging = ( | ||
self.env["product.packaging"].browse(vals["product_packaging_id"]) | ||
if "product_packaging_id" in vals | ||
else self.product_packaging_id | ||
) | ||
|
||
return product._convert_purchase_packaging_qty( | ||
quantity, | ||
uom or product.uom_po_id, | ||
packaging=packaging, | ||
) | ||
|
||
@api.depends("product_id", "product_uom_qty", "product_uom") | ||
def _compute_product_packaging_id(self): | ||
for rec in self: | ||
rec.product_packaging_id = False | ||
rec._force_packaging() | ||
rec._force_qty_with_package() | ||
return super()._compute_product_packaging_id() | ||
|
||
def _get_product_packaging_having_multiple_qty(self, product, qty, uom): | ||
if uom != product.uom_po_id: | ||
qty = uom._compute_quantity(qty, product.uom_po_id) | ||
return product.get_first_purchase_packaging_with_multiple_qty(qty) | ||
|
||
def _inverse_product_packaging_qty(self): | ||
# Force skipping of auto assign | ||
# if we are writing the product_qty directly via inverse | ||
return super( | ||
PurchaseOrderLine, self.with_context(_skip_auto_assign=True) | ||
)._inverse_product_packaging_qty() | ||
|
||
def write(self, vals): | ||
"""Auto assign packaging if needed""" | ||
if vals.get("product_packaging_id") or self.env.context.get( | ||
"_skip_auto_assign" | ||
): | ||
# setting the packaging directly, skip auto assign | ||
return super().write(vals) | ||
for line in self: | ||
line_vals = vals.copy() | ||
if line_vals.get("product_id", False): | ||
packaging = line._get_autoassigned_packaging(line_vals) | ||
if packaging: | ||
line_vals.update({"product_packaging_id": packaging}) | ||
if ( | ||
line_vals.get("product_qty") | ||
or line_vals.get("product_id") | ||
or line_vals.get("product_packaging_id") | ||
or line_vals.get("product_uom") | ||
): | ||
product_qty = line.get_packaging_qty(line_vals) | ||
line_vals.update({"product_qty": product_qty}) | ||
super(PurchaseOrderLine, line).write(line_vals) | ||
return True | ||
|
||
@api.model_create_multi | ||
def create(self, vals_list): | ||
"""Auto assign packaging if needed""" | ||
# Fill the packaging if they are empty and the quantity is a multiple | ||
for vals in vals_list: | ||
if not vals.get("product_packaging_id"): | ||
if "product_qty" not in vals: | ||
vals["product_qty"] = 1.0 | ||
packaging = self._get_autoassigned_packaging(vals) | ||
if packaging: | ||
vals.update({"product_packaging_id": packaging}) | ||
if vals.get("product_id") and vals.get("product_uom"): | ||
product_qty = self.get_packaging_qty(vals) | ||
vals.update({"product_qty": product_qty}) | ||
return super().create(vals_list) | ||
|
||
def _get_autoassigned_packaging(self, vals=None): | ||
if not vals: | ||
vals = [] | ||
product = ( | ||
self.env["product.product"].browse(vals["product_id"]) | ||
if "product_id" in vals | ||
else self.product_id | ||
) | ||
if product and product.purchase_only_by_packaging: | ||
quantity = ( | ||
vals["product_qty"] if "product_qty" in vals else self.product_qty | ||
) | ||
uom = ( | ||
self.env["uom.uom"].browse(vals["product_uom"]) | ||
if "product_uom" in vals | ||
else self.product_uom | ||
) | ||
packaging = self._get_product_packaging_having_multiple_qty( | ||
product, quantity, uom | ||
) | ||
if packaging: | ||
return packaging.id | ||
return None | ||
|
||
def _force_packaging(self): | ||
if not self.product_packaging_id and self.product_id.purchase_only_by_packaging: | ||
packaging_id = self._get_autoassigned_packaging() | ||
if packaging_id: | ||
self.update({"product_packaging_id": packaging_id}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Following options are available to define which packaging type can be purchased and | ||
which product can only be purchased by packaging. | ||
|
||
* Can be purchased: On product packaging type model, this checkbox defines if product | ||
packagings from this particular type are available to be selected on purchase | ||
order line. | ||
|
||
* Purchase only by packaging: On product template model, this checkbox restricts | ||
purchases of these products if no packaging is selected on the purchase order line. | ||
If no packaging is selected, it will either be auto-assigned if the quantity | ||
on the purchase order line matches a packaging quantity or an error will be raised. | ||
|
||
* Force purchase quantity (on the packaging): force rounds up the quantity during | ||
creation/modification of the purchase order line with the factor set on the packaging. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Bastian Guenther <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
This module provides different configuration option to manage packagings on | ||
purchase orders. | ||
|
||
The creation/update of purchase order line will be blocked (by constraints) if the data | ||
on the purchase.order.line does not fit with the configuration of the product's | ||
packagings. | ||
|
||
It's also possible to force the quantity to purchase during creation/modification of | ||
the purchase order line if the "Force purchase quantity" is ticked on the packaging. | ||
|
||
For example, if your packaging is set to purchase by 5 units and the employee fill | ||
the quantity with 3, the quantity will be automatically replaced by 5 (it always | ||
rounds up). |
Oops, something went wrong.