Merge PR #2115 into 16.0

Signed-off-by rousseldenis
This commit is contained in:
OCA-git-bot
2024-07-29 06:56:34 +00:00
9 changed files with 117 additions and 102 deletions

View File

@@ -62,8 +62,8 @@ If you want to transfer a full quant:
- Select the quantities which you want move to another location
If you go to the Inventory Dashboard you can see the button "Move from
location" in each of the picking types (only applicable to internal
transfers). Press it and you will be directed to the wizard.
location" in each of the picking types (only applicable to internal and
outgoing transfers). Press it and you will be directed to the wizard.
|image1|
@@ -143,6 +143,8 @@ Contributors
- Aung Ko Ko Lin
- Laurent Mignon <laurent.mignon@acsone.eu>
Maintainers
-----------

View File

@@ -13,3 +13,4 @@
- Abraham Anes \<<abraham@studio73.es>\>
- Quartile \<<https://www.quartile.co>\>
- Aung Ko Ko Lin
- Laurent Mignon \<<laurent.mignon@acsone.eu>\>

View File

@@ -1,43 +1,37 @@
- A new menu item Operations \> Move from location... opens a wizard
where 2 locations can be specified.
- Select origin and destination locations and press "IMMEDIATE TRANSFER"
or "PLANNED TRANSFER"
- A new menu item Operations \> Move from location... opens a wizard where 2 locations
can be specified.
- Select origin and destination locations and press "IMMEDIATE TRANSFER" or "PLANNED
TRANSFER"
- Press ADD ALL button to add all products available
- Those lines can be edited. Move quantity can't be more than a max
available quantity
- Those lines can be edited. Move quantity can't be more than a max available quantity
- Move doesn't care about the reservations and will move stuff anyway
- If during your operation with the wizard the real quantity will change
it will move only the available quantity at the button press
- Products will be moved and a form view of picking that did that will
show up
- If "PLANNED TRANSFER" is used - the picking won't be validated
automatically
- If during your operation with the wizard the real quantity will change it will move
only the available quantity at the button press
- Products will be moved and a form view of picking that did that will show up
- If "PLANNED TRANSFER" is used - the picking won't be validated automatically
If you want to transfer a full quant:
- Go to Inventory \> Products \> Products and click "On hand" smart
button or Inventory \> Reporting \> Inventory, the quants view will be
opened.
- Go to Inventory \> Products \> Products and click "On hand" smart button or Inventory
\> Reporting \> Inventory, the quants view will be opened.
- Select the quantities which you want move to another location
If you go to the Inventory Dashboard you can see the button "Move from
location" in each of the picking types (only applicable to internal
transfers). Press it and you will be directed to the wizard.
If you go to the Inventory Dashboard you can see the button "Move from location" in each
of the picking types (only applicable to internal and outgoing transfers). Press it and
you will be directed to the wizard.
![](https://user-images.githubusercontent.com/147538094/281480833-208ea309-0bad-43e7-bd6f-8384520afe00.png)
To enable this option, check "Show Move On Hand Stock" in the Picking Type configuration.
To enable this option, check "Show Move On Hand Stock" in the Picking Type
configuration.
![](https://user-images.githubusercontent.com/147538094/281479487-45fa4bde-36be-4ba1-8d54-8e707b89459e.png)
If you want transfer everything from stock.location
On a draft picking, add a button to fill with moves lines for all
products in the source destination. This allows to create a picking to
move all the content of a location. The Origin Location must have stock.
The Destination Location has to be a final location.
If some quants are not available
(i.e. reserved) the picking will be in partially available state and
reserved moves won't be listed in the operations. Use barcode interface
to scan a location and create an empty picking. Then use the fill with
stock button.
On a draft picking, add a button to fill with moves lines for all products in the source
destination. This allows to create a picking to move all the content of a location. The
Origin Location must have stock. The Destination Location has to be a final location. If
some quants are not available (i.e. reserved) the picking will be in partially available
state and reserved moves won't be listed in the operations. Use barcode interface to
scan a location and create an empty picking. Then use the fill with stock button.

View File

@@ -412,8 +412,8 @@ opened.</li>
<li>Select the quantities which you want move to another location</li>
</ul>
<p>If you go to the Inventory Dashboard you can see the button “Move from
location” in each of the picking types (only applicable to internal
transfers). Press it and you will be directed to the wizard.</p>
location” in each of the picking types (only applicable to internal and
outgoing transfers). Press it and you will be directed to the wizard.</p>
<p><img alt="image1" src="https://user-images.githubusercontent.com/147538094/281480833-208ea309-0bad-43e7-bd6f-8384520afe00.png" /></p>
<p>To enable this option, check “Show Move On Hand Stock” in the Picking
Type configuration.</p>
@@ -483,6 +483,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<li>Aung Ko Ko Lin</li>
</ul>
</li>
<li>Laurent Mignon &lt;<a class="reference external" href="mailto:laurent.mignon&#64;acsone.eu">laurent.mignon&#64;acsone.eu</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">

View File

@@ -92,46 +92,49 @@ class TestsCommon(common.TransactionCase):
)
cls.package = cls.env["stock.quant.package"].create({})
cls.package1 = cls.env["stock.quant.package"].create({})
cls.package2 = cls.env["stock.quant.package"].create({})
def setup_product_amounts(self):
self.set_product_amount(self.product_no_lots, self.internal_loc_1, 123)
self.set_product_amount(
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot1
@classmethod
def setup_product_amounts(cls):
cls.set_product_amount(cls.product_no_lots, cls.internal_loc_1, 123)
cls.set_product_amount(
cls.product_lots, cls.internal_loc_1, 1.0, lot_id=cls.lot1
)
self.set_product_amount(
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot2
cls.set_product_amount(
cls.product_lots, cls.internal_loc_1, 1.0, lot_id=cls.lot2
)
self.set_product_amount(
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot3
cls.set_product_amount(
cls.product_lots, cls.internal_loc_1, 1.0, lot_id=cls.lot3
)
self.set_product_amount(
self.product_package,
self.internal_loc_1,
cls.set_product_amount(
cls.product_package,
cls.internal_loc_1,
1.0,
lot_id=self.lot4,
package_id=self.package,
lot_id=cls.lot4,
package_id=cls.package,
)
self.set_product_amount(
self.product_package,
self.internal_loc_1,
cls.set_product_amount(
cls.product_package,
cls.internal_loc_1,
1.0,
lot_id=self.lot4,
package_id=self.package1,
lot_id=cls.lot4,
package_id=cls.package1,
)
self.set_product_amount(
self.product_package,
self.internal_loc_1,
cls.set_product_amount(
cls.product_package,
cls.internal_loc_1,
1.0,
lot_id=self.lot5,
package_id=self.package2,
owner_id=self.partner,
lot_id=cls.lot5,
package_id=cls.package2,
owner_id=cls.partner,
)
@classmethod
def set_product_amount(
self, product, location, amount, lot_id=None, package_id=None, owner_id=None
cls, product, location, amount, lot_id=None, package_id=None, owner_id=None
):
self.env["stock.quant"]._update_available_quantity(
cls.env["stock.quant"]._update_available_quantity(
product,
location,
amount,

View File

@@ -8,9 +8,10 @@ from .test_common import TestsCommon
class TestMoveLocation(TestsCommon):
def setUp(self):
super().setUp()
self.setup_product_amounts()
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.setup_product_amounts()
def test_move_location_wizard(self):
"""Test a simple move."""

View File

@@ -8,7 +8,7 @@
<field name="show_operations" position="after">
<field
name="show_move_onhand"
attrs='{"invisible": [("code", "not in", ["internal"])]}'
attrs='{"invisible": [("code", "not in", ["internal", "outgoing"])]}'
/>
</field>
</field>

View File

@@ -71,7 +71,7 @@ class StockMoveLocationWizard(models.TransientModel):
for rec in self:
picking_type = self.env["stock.picking.type"]
base_domain = [
("code", "=", "internal"),
("code", "in", ("internal", "outgoing")),
("warehouse_id.company_id", "=", company_id),
]
if rec.origin_location_id:
@@ -303,25 +303,32 @@ class StockMoveLocationWizard(models.TransientModel):
)
return action
def _get_quants_domain(self):
return [("location_id", "=", self.origin_location_id.id)]
def _get_group_quants(self):
location_id = self.origin_location_id
# Using sql as search_group doesn't support aggregation functions
# leading to overhead in queries to DB
query = """
SELECT product_id, lot_id, package_id, owner_id, SUM(quantity) AS quantity,
SUM(reserved_quantity) AS reserved_quantity
FROM stock_quant
WHERE location_id = %s
GROUP BY product_id, lot_id, package_id, owner_id
"""
self.env.cr.execute(query, (location_id.id,))
return self.env.cr.dictfetchall()
domain = self._get_quants_domain()
result = self.env["stock.quant"].read_group(
domain=domain,
fields=[
"product_id",
"lot_id",
"package_id",
"owner_id",
"quantity:sum",
"reserved_quantity:sum",
],
groupby=["product_id", "lot_id", "package_id", "owner_id"],
orderby="id",
lazy=False,
)
return result
def _get_stock_move_location_lines_values(self):
product_obj = self.env["product.product"]
product_data = []
for group in self._get_group_quants():
product = product_obj.browse(group.get("product_id")).exists()
product = product_obj.browse(group["product_id"][0]).exists()
# Apply the putaway strategy
location_dest_id = (
self.apply_putaway_strategy
@@ -337,15 +344,33 @@ class StockMoveLocationWizard(models.TransientModel):
"origin_location_id": self.origin_location_id.id,
"destination_location_id": location_dest_id,
# cursor returns None instead of False
"lot_id": group.get("lot_id") or False,
"package_id": group.get("package_id") or False,
"owner_id": group.get("owner_id") or False,
"lot_id": group["lot_id"][0] if group.get("lot_id") else False,
"package_id": group["package_id"][0]
if group.get("package_id")
else False,
"owner_id": group["owner_id"][0]
if group.get("owner_id")
else False,
"product_uom_id": product.uom_id.id,
"custom": False,
}
)
return product_data
def _reset_stock_move_location_lines(self):
lines = []
line_model = self.env["wiz.stock.move.location.line"]
for line_val in self._get_stock_move_location_lines_values():
if line_val.get("max_quantity") <= 0:
continue
line = line_model.create(line_val)
line.max_quantity = line.get_max_quantity()
line.reserved_quantity = line.reserved_quantity
lines.append(line)
self.update(
{"stock_move_location_line_ids": [(6, 0, [line.id for line in lines])]}
)
@api.onchange("origin_location_id")
def onchange_origin_location(self):
# Get origin_location_disable context key to prevent load all origin
@@ -355,18 +380,7 @@ class StockMoveLocationWizard(models.TransientModel):
not self.env.context.get("origin_location_disable")
and self.origin_location_id
):
lines = []
line_model = self.env["wiz.stock.move.location.line"]
for line_val in self._get_stock_move_location_lines_values():
if line_val.get("max_quantity") <= 0:
continue
line = line_model.create(line_val)
line.max_quantity = line.get_max_quantity()
line.reserved_quantity = line.reserved_quantity
lines.append(line)
self.update(
{"stock_move_location_line_ids": [(6, 0, [line.id for line in lines])]}
)
self._reset_stock_move_location_lines()
def clear_lines(self):
self._clear_lines()

View File

@@ -10,18 +10,15 @@
<form>
<sheet>
<div class="oe_button_box" name="button_box" />
<div>
<label for="edit_locations">
Edit Locations
</label>
<group name="options">
<field name="edit_locations" widget="boolean_toggle" />
</div>
<div groups="stock.group_stock_multi_locations">
<label for="apply_putaway_strategy">
Apply putaway strategy for moving products
</label>
<field name="apply_putaway_strategy" widget="boolean_toggle" />
</div>
<field
name="apply_putaway_strategy"
string="Apply putaway strategy for moving products"
widget="boolean_toggle"
groups="stock.group_stock_multi_locations"
/>
</group>
<group name="picking_type">
<field name="picking_type_id" />
</group>
@@ -37,6 +34,8 @@
attrs="{'readonly': [('destination_location_disable', '=', True)]}"
/>
</group>
<group name="filters">
</group>
<group name="lines">
<field
name="stock_move_location_line_ids"