mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
@@ -0,0 +1 @@
|
||||
../../../../stock_picking_orig_dest_link
|
||||
6
setup/stock_picking_orig_dest_link/setup.py
Normal file
6
setup/stock_picking_orig_dest_link/setup.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['setuptools-odoo'],
|
||||
odoo_addon=True,
|
||||
)
|
||||
2
stock_picking_orig_dest_link/__init__.py
Normal file
2
stock_picking_orig_dest_link/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import models
|
||||
from .hooks import pre_init_hook, post_init_hook
|
||||
18
stock_picking_orig_dest_link/__manifest__.py
Normal file
18
stock_picking_orig_dest_link/__manifest__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
{
|
||||
"name": "Stock Picking Origin Destination Link",
|
||||
"summary": """
|
||||
This addon link the pickings with their respective Origin and Destination Pickings.
|
||||
""",
|
||||
"version": "13.0.1.0.0",
|
||||
"author": "ForgeFlow, Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||
"category": "Warehouse Management",
|
||||
"license": "AGPL-3",
|
||||
"depends": ["stock"],
|
||||
"data": ["views/stock_picking_views.xml"],
|
||||
"installable": True,
|
||||
"pre_init_hook": "pre_init_hook",
|
||||
"post_init_hook": "post_init_hook",
|
||||
}
|
||||
41
stock_picking_orig_dest_link/hooks.py
Normal file
41
stock_picking_orig_dest_link/hooks.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright 2023 ForgeFlow, S.L.
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
|
||||
from odoo import SUPERUSER_ID, api
|
||||
from odoo.tools import sql
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def pre_init_hook(cr):
|
||||
_logger.info(
|
||||
"Create temporary table to avoid the automatic launch of the compute method"
|
||||
)
|
||||
if not sql.table_exists(cr, "stock_picking_orig_dest_rel"):
|
||||
cr.execute("CREATE TABLE stock_picking_orig_dest_rel (temp INTEGER)")
|
||||
|
||||
|
||||
def post_init_hook(cr, registry):
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
_logger.info("Dropping temporary table")
|
||||
cr.execute("DROP TABLE stock_picking_orig_dest_rel")
|
||||
_logger.info("Creating new table via ORM")
|
||||
StockPicking = env["stock.picking"]
|
||||
StockPicking._fields["orig_picking_ids"].update_db(StockPicking, False)
|
||||
StockPicking._fields["dest_picking_ids"].update_db(StockPicking, False)
|
||||
_logger.info("Filling Origin and Destination Picking relation table")
|
||||
query = """
|
||||
WITH query AS (
|
||||
SELECT spo.id AS orig_picking_id, spd.id AS dest_picking_id
|
||||
FROM stock_move_move_rel smml
|
||||
JOIN stock_move smo ON smo.id = smml.move_orig_id
|
||||
JOIN stock_move smd ON smd.id = smml.move_dest_id
|
||||
JOIN stock_picking spo ON smo.picking_id = spo.id
|
||||
JOIN stock_picking spd ON smd.picking_id = spd.id
|
||||
GROUP BY spo.id, spd.id
|
||||
)
|
||||
INSERT INTO stock_picking_orig_dest_rel (orig_picking_id, dest_picking_id)
|
||||
SELECT * FROM query;
|
||||
"""
|
||||
cr.execute(query)
|
||||
1
stock_picking_orig_dest_link/models/__init__.py
Normal file
1
stock_picking_orig_dest_link/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import stock_picking
|
||||
51
stock_picking_orig_dest_link/models/stock_picking.py
Normal file
51
stock_picking_orig_dest_link/models/stock_picking.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
orig_picking_ids = fields.Many2many(
|
||||
comodel_name="stock.picking",
|
||||
relation="stock_picking_orig_dest_rel",
|
||||
column1="dest_picking_id",
|
||||
column2="orig_picking_id",
|
||||
string="Origin Transfer/s",
|
||||
compute="_compute_origin_dest_picking",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
dest_picking_ids = fields.Many2many(
|
||||
comodel_name="stock.picking",
|
||||
relation="stock_picking_orig_dest_rel",
|
||||
column1="orig_picking_id",
|
||||
column2="dest_picking_id",
|
||||
string="Destination Transfer/s",
|
||||
compute="_compute_origin_dest_picking",
|
||||
store=True,
|
||||
readonly=True,
|
||||
)
|
||||
|
||||
def _get_orig_picking_ids(self):
|
||||
"""
|
||||
Returns the Origin Pickings from a single picking. Done in method to be
|
||||
inherited in other modules, if needed.
|
||||
"""
|
||||
self.ensure_one()
|
||||
return self.mapped("move_lines.move_orig_ids.picking_id")
|
||||
|
||||
def _get_dest_picking_ids(self):
|
||||
"""
|
||||
Returns the Destination Pickings from a single picking. Done in method to be
|
||||
inherited in other modules, if needed.
|
||||
"""
|
||||
self.ensure_one()
|
||||
return self.mapped("move_lines.move_dest_ids.picking_id")
|
||||
|
||||
@api.depends("move_lines.move_orig_ids", "move_lines.move_dest_ids")
|
||||
def _compute_origin_dest_picking(self):
|
||||
for picking in self:
|
||||
picking.orig_picking_ids = picking._get_orig_picking_ids()
|
||||
picking.dest_picking_ids = picking._get_dest_picking_ids()
|
||||
2
stock_picking_orig_dest_link/readme/CONTRIBUTORS.rst
Normal file
2
stock_picking_orig_dest_link/readme/CONTRIBUTORS.rst
Normal file
@@ -0,0 +1,2 @@
|
||||
* `ForgeFlow <https://www.forgeflow.com>`
|
||||
* Guillem Casassas <guillem.casassas@forgeflow.com>
|
||||
5
stock_picking_orig_dest_link/readme/DESCRIPTION.rst
Normal file
5
stock_picking_orig_dest_link/readme/DESCRIPTION.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
This module adds the link between the origin and the destination pickings through a technical field. It can latter be added in the views to show it to the user.
|
||||
|
||||
For each picking in the system, it will link it to their respective Origin and Destination pickings.
|
||||
|
||||
It also adds the possibility to view the Origin or Destination of a certain transfer in the list view by adding two additional columns which are hidden by default. Also, you can filter and group by the transfers.
|
||||
1
stock_picking_orig_dest_link/readme/ROADMAP.rst
Normal file
1
stock_picking_orig_dest_link/readme/ROADMAP.rst
Normal file
@@ -0,0 +1 @@
|
||||
* This module is very similar to *stock_picking_show_linked* in terms of funcionalities being added. In future versions, we should consider merging the two modules into a single one.
|
||||
1
stock_picking_orig_dest_link/tests/__init__.py
Normal file
1
stock_picking_orig_dest_link/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import test_stock_picking_orig_dest_link
|
||||
@@ -0,0 +1,87 @@
|
||||
# Copyright 2023 ForgeFlow S.L. (https://www.forgeflow.com)
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo.tests.common import SavepointCase
|
||||
|
||||
|
||||
class TestPickingOrigDestLink(SavepointCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
||||
# Models
|
||||
cls.product_model = cls.env["product.product"]
|
||||
cls.picking_model = cls.env["stock.picking"]
|
||||
cls.partner_model = cls.env["res.partner"]
|
||||
cls.move_model = cls.env["stock.move"]
|
||||
|
||||
# Created records
|
||||
cls.product = cls.product_model.create({"name": "Test Product"})
|
||||
cls.partner = cls.partner_model.create({"name": "Test Partner"})
|
||||
|
||||
# Data records
|
||||
cls.pick_type_in = cls.env.ref("stock.picking_type_in")
|
||||
cls.pick_type_out = cls.env.ref("stock.picking_type_out")
|
||||
cls.stock_location = cls.env.ref("stock.stock_location_stock")
|
||||
cls.customers_location = cls.env.ref("stock.stock_location_customers")
|
||||
cls.suppliers_location = cls.env.ref("stock.stock_location_suppliers")
|
||||
|
||||
@classmethod
|
||||
def _create_in_picking(cls):
|
||||
picking = cls.picking_model.create(
|
||||
{
|
||||
"partner_id": cls.partner.id,
|
||||
"picking_type_id": cls.pick_type_in.id,
|
||||
"location_id": cls.suppliers_location.id,
|
||||
"location_dest_id": cls.stock_location.id,
|
||||
}
|
||||
)
|
||||
move_vals = {
|
||||
"picking_id": picking.id,
|
||||
"product_id": cls.product.id,
|
||||
"location_dest_id": cls.stock_location.id,
|
||||
"location_id": cls.suppliers_location.id,
|
||||
"name": cls.product.name,
|
||||
"product_uom_qty": 1,
|
||||
"product_uom": cls.product.uom_id.id,
|
||||
}
|
||||
cls.move_model.create(move_vals)
|
||||
return picking
|
||||
|
||||
@classmethod
|
||||
def _create_out_picking(cls):
|
||||
picking = cls.picking_model.create(
|
||||
{
|
||||
"partner_id": cls.partner.id,
|
||||
"picking_type_id": cls.pick_type_out.id,
|
||||
"location_id": cls.stock_location.id,
|
||||
"location_dest_id": cls.customers_location.id,
|
||||
}
|
||||
)
|
||||
move_vals = {
|
||||
"picking_id": picking.id,
|
||||
"product_id": cls.product.id,
|
||||
"location_id": cls.stock_location.id,
|
||||
"location_dest_id": cls.customers_location.id,
|
||||
"name": cls.product.name,
|
||||
"product_uom_qty": 1,
|
||||
"product_uom": cls.product.uom_id.id,
|
||||
}
|
||||
cls.move_model.create(move_vals)
|
||||
return picking
|
||||
|
||||
@classmethod
|
||||
def _link_moves_from_pickings(cls, in_picking, out_picking):
|
||||
in_move_ids = in_picking.move_lines
|
||||
out_move_ids = out_picking.move_lines
|
||||
return in_move_ids.write({"move_dest_ids": out_move_ids})
|
||||
|
||||
def test_01_in_out_pickings(self):
|
||||
# create in and out picking and links their moves
|
||||
in_pick = self._create_in_picking()
|
||||
out_pick = self._create_out_picking()
|
||||
result = self._link_moves_from_pickings(in_pick, out_pick)
|
||||
self.assertTrue(result)
|
||||
# check that pickings have also been linked
|
||||
self.assertEqual(in_pick.dest_picking_ids, out_pick)
|
||||
self.assertEqual(out_pick.orig_picking_ids, in_pick)
|
||||
37
stock_picking_orig_dest_link/views/stock_picking_views.xml
Normal file
37
stock_picking_orig_dest_link/views/stock_picking_views.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<data>
|
||||
<record id="view_stock_picking_tree" model="ir.ui.view">
|
||||
<field name="name">stock.picking.tree - stock_picking_orig_dest_link</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.vpicktree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="activity_exception_decoration" position="after">
|
||||
<field
|
||||
name="orig_picking_ids"
|
||||
widget="many2many_tags"
|
||||
optional="hide"
|
||||
/>
|
||||
<field
|
||||
name="dest_picking_ids"
|
||||
widget="many2many_tags"
|
||||
optional="hide"
|
||||
/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_picking_internal_search" model="ir.ui.view">
|
||||
<field
|
||||
name="name"
|
||||
>stock.picking.search - stock_picking_orig_dest_link</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_internal_search" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="picking_type_id" position="after">
|
||||
<field name="orig_picking_ids" />
|
||||
<field name="dest_picking_ids" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user