Skip to content

Commit

Permalink
[16.0][ADD] stock_pallet_filling
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkhao committed Jul 16, 2024
1 parent d104027 commit 268130f
Show file tree
Hide file tree
Showing 17 changed files with 339 additions and 3 deletions.
7 changes: 4 additions & 3 deletions account_move_adyen_import/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

/*
:Author: David Goodger ([email protected])
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.

See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
Expand Down Expand Up @@ -274,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }

pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
Expand All @@ -300,7 +301,7 @@
span.pre {
white-space: pre }

span.problematic {
span.problematic, pre.problematic {
color: red }

span.section-subtitle {
Expand Down
6 changes: 6 additions & 0 deletions setup/stock_pallet_filling/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
23 changes: 23 additions & 0 deletions stock_pallet_filling/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
====================
Stock Pallet Filling
====================

Calculate pallet fillings from product volumes on pickings and sale orders

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

To configure this module, you need to:

#. Go to ...

Usage
=====

To use this module, you need to:

#. Go to ...


Changelog
=========
1 change: 1 addition & 0 deletions stock_pallet_filling/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
19 changes: 19 additions & 0 deletions stock_pallet_filling/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Stock Pallet Filling",
"summary": """
Calculate pallet fillings from product volumes on pickings and sale orders""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Akretion",
"website": "https://github.com/akretion/ak-odoo-incubator",
"depends": ["sale_stock"],
"data": [
"views/stock_package_type.xml",
"views/stock_picking.xml",
"views/sale_order.xml",
],
"demo": [],
}
4 changes: 4 additions & 0 deletions stock_pallet_filling/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import sale_order
from . import stock_picking
from . import stock_package_type
from . import pallet_filling_mixin
2 changes: 2 additions & 0 deletions stock_pallet_filling/models/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
71 changes: 71 additions & 0 deletions stock_pallet_filling/models/pallet_filling_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

import math

from odoo import api, fields, models

STD_PALLET_VOLUME = 1650 * 800 * 1200


class PalletFillingMixin(models.AbstractModel):

_name = "pallet.filling.mixin"
_description = "Pallet Filling Mixin" # TODO

pallet_estimation = fields.Integer(
string="Nombre de palettes estimées",
help="Estimation du nombre de palettes pour cette commande",
compute="_compute_pallet_estimation",
store=False,
)
pallet_estimation_warning = fields.Char(
string="Avertissement",
compute="_compute_pallet_estimation",
store=False,
)

@api.model
def _get_pallet_filling_constants(self):
return STD_PALLET_VOLUME, 0.75

def _get_pkgs_total_volume(self, lines):
total_volume = 0
for line in lines:
pkg_nbr = math.ceil(line.product_uom_qty / line.product_packaging_id.qty)
total_volume += pkg_nbr * line.product_packaging_id.package_type_id.volume
return total_volume

def _pallet_filling_calc(self): # TODO optimize perf ?
lines = self[self._pallet_line_fieldname]
lines_without_packaging = lines.filtered(
lambda r: not r.product_packaging_id.package_type_id
or not r.product_packaging_id
)
if lines_without_packaging:
warning = (
f"Les articles suivants n'ont pas de"
f" colisage pour calcul du remplissage "
f"de palette: "
f"{lines_without_packaging.mapped('product_id.default_code')}"
)
else:
warning = ""
lines_with_packaging = lines - lines_without_packaging
pallet_volume, filling_rate = self._get_pallet_filling_constants()
pallet_estimation = lines_without_packaging.ceil(
self._get_pkgs_total_volume(lines_with_packaging)
/ (pallet_volume * (filling_rate / 100))
)
return pallet_estimation, warning

def _compute_pallet_estimation(self):
for rec in self:
(
rec.pallet_estimation,
rec.pallet_estimation_warning,
) = rec._pallet_filling_calc()

@property
def _pallet_line_fieldname(self):
raise NotImplementedError
22 changes: 22 additions & 0 deletions stock_pallet_filling/models/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


class SaleOrder(models.Model):

_inherit = ["sale.order", "pallet.filling.mixin"]
_name = "sale.order"

@api.depends(
"order_line.product_uom_qty",
"order_line.product_packaging_id",
"order_line.product_packaging_id.package_type_id.volume",
)
def _compute_pallet_estimation(self):
return super()._compute_pallet_estimation()

@property
def _pallet_line_fieldname(self):
return "order_line"
18 changes: 18 additions & 0 deletions stock_pallet_filling/models/stock_package_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class StockPackageType(models.Model):

_inherit = "stock.package.type"

volume = fields.Float(
compute="_compute_volume", store=True, help="Volume en millimètres cube"
)

@api.depends("packaging_length", "width", "height")
def _compute_volume(self):
for rec in self:
rec.volume = rec.packaging_length * rec.width * rec.height
22 changes: 22 additions & 0 deletions stock_pallet_filling/models/stock_picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


class StockPicking(models.Model):

_inherit = ["stock.picking", "pallet.filling.mixin"]
_name = "stock.picking"

@api.depends(
"move_ids.product_uom_qty",
"move_ids.product_packaging_id",
"move_ids.product_packaging_id.package_type_id.volume",
)
def _compute_pallet_estimation(self):
return super()._compute_pallet_estimation()

@property
def _pallet_line_fieldname(self):
return "move_ids"
1 change: 1 addition & 0 deletions stock_pallet_filling/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_pallet_filling_estimation
82 changes: 82 additions & 0 deletions stock_pallet_filling/tests/test_pallet_filling_estimation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2024 Akretion
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo.tests.common import TransactionCase


class TestPalletFillingEstimation(TransactionCase):
def setUp(self):
super().setUp()
cpny = self.env.ref("base.main_company")
self.prd_1 = self.env["product.product"].create(
{
"name": "PRD1",
"default_code": "PRD1",
"company_id": cpny.id,
}
)
self.prd_2 = self.env["product.product"].create(
{
"name": "PRD2",
"default_code": "PRD2",
"company_id": cpny.id,
}
)
self.pkg_type = self.env["stock.package.type"].create(
{
"name": "Test Package Type",
"packaging_length": 1000,
"width": 1000,
"height": 1000,
}
)

def get_pack_vals(product):
return {
"product_id": product.id,
"name": "any",
"qty": 50,
"company_id": self.env.company.id,
"package_type_id": self.pkg_type.id,
}

vals_list = [
get_pack_vals(self.prd_2),
get_pack_vals(self.prd_1),
]
self.env["product.packaging"].create(vals_list)

def get_line_vals(product):
return {
"name": product.name,
"product_id": product.id,
"product_packaging_qty": 20,
"product_packaging_id": product.packaging_ids[0].id,
"product_qty": 1000, # onchange not played
}

so_vals = {
# gemini
"partner_id": self.env.ref("base.res_partner_3").id,
"order_line": [
(0, 0, get_line_vals(self.prd_2)),
(0, 0, get_line_vals(self.prd_1)),
],
}
self.so_gemi = self.env["sale.order"].create(so_vals)
self.so_gemi.action_confirm()
self.picking = self.so_gemi.picking_ids

def test_calculate_pallet_filling(self):
# 1000 units, 50 per pack => 20 m3 packages per product
# 40 m3 / 1.188 m3 = 34 pallets
self.assertEqual(self.picking.pallet_estimation, 34)
self.assertEqual(self.so_gemi.pallet_estimation, 34)

def test_calculate_pallet_filling_error(self):
# 20 m3 / 1.188 m3 = 17 pallets
self.so_gemi.order_line[0].product_packaging_id = False
self.assertEqual(self.picking.pallet_estimation, 17)
self.assertTrue(self.picking.pallet_estimation_warning)
self.assertEqual(self.so_gemi.pallet_estimation, 17)
self.assertTrue(self.so_gemi.pallet_estimation_warning)
21 changes: 21 additions & 0 deletions stock_pallet_filling/views/sale_order.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Akretion
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>

<record model="ir.ui.view" id="sale_order_form_view">
<field name="name">sale.order.form (stock_pallet_filling)</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<field name="delivery_status" position="after">
<field name="pallet_estimation" />
<field
name="pallet_estimation_warning"
attrs="{'invisible': [('pallet_estimation_warning', '=', False)]}"
/>
</field>
</field>
</record>

</odoo>
19 changes: 19 additions & 0 deletions stock_pallet_filling/views/stock_package_type.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Akretion
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>

<record model="ir.ui.view" id="stock_package_type_form_view">
<field name="name">stock.package.type.form (in stock_c)</field>
<field name="model">stock.package.type</field>
<field name="inherit_id" ref="stock.stock_package_type_form" />
<field name="arch" type="xml">
<field name="barcode" position="after">
<field name="volume" />
</field>
</field>
</record>



</odoo>
23 changes: 23 additions & 0 deletions stock_pallet_filling/views/stock_picking.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Akretion
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>

<record id="view_picking_form" model="ir.ui.view">
<field name="name">stock.picking.form (stock_pallet_filling)</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form" />
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="pallet_estimation" />
<field
name="pallet_estimation_warning"
attrs="{'invisible': [('pallet_estimation_warning', '=', False)]}"
/>
</field>
</field>
</record>



</odoo>

0 comments on commit 268130f

Please sign in to comment.