mirror of
https://github.com/ForgeFlow/stock-rma.git
synced 2025-01-21 12:57:49 +02:00
[14.0][FIX] rma, add support to handle package used on customer moves
This commit is contained in:
@@ -11,3 +11,4 @@ from . import stock_rule
|
|||||||
from . import res_partner
|
from . import res_partner
|
||||||
from . import res_company
|
from . import res_company
|
||||||
from . import res_config_settings
|
from . import res_config_settings
|
||||||
|
from . import stock_package_level
|
||||||
|
|||||||
@@ -523,6 +523,10 @@ class RmaOrderLine(models.Model):
|
|||||||
string="Under Warranty?", readonly=True, states={"draft": [("readonly", False)]}
|
string="Under Warranty?", readonly=True, states={"draft": [("readonly", False)]}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _get_stock_move_reference(self):
|
||||||
|
self.ensure_one()
|
||||||
|
return self.reference_move_id
|
||||||
|
|
||||||
def _prepare_rma_line_from_stock_move(self, sm, lot=False):
|
def _prepare_rma_line_from_stock_move(self, sm, lot=False):
|
||||||
if not self.type:
|
if not self.type:
|
||||||
self.type = self._get_default_type()
|
self.type = self._get_default_type()
|
||||||
|
|||||||
@@ -110,3 +110,16 @@ class StockMove(models.Model):
|
|||||||
res = super(StockMove, self)._prepare_procurement_values()
|
res = super(StockMove, self)._prepare_procurement_values()
|
||||||
res["rma_line_id"] = self.rma_line_id.id
|
res["rma_line_id"] = self.rma_line_id.id
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class StockMoveLine(models.Model):
|
||||||
|
|
||||||
|
_inherit = "stock.move.line"
|
||||||
|
|
||||||
|
def _should_bypass_reservation(self, location):
|
||||||
|
res = super(StockMoveLine, self)._should_bypass_reservation(location)
|
||||||
|
if self.env.context.get(
|
||||||
|
"force_no_bypass_reservation"
|
||||||
|
) and location.usage not in ("customer", "supplier"):
|
||||||
|
return False
|
||||||
|
return res
|
||||||
|
|||||||
20
rma/models/stock_package_level.py
Normal file
20
rma/models/stock_package_level.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class StockPackageLevel(models.Model):
|
||||||
|
|
||||||
|
_inherit = "stock.package_level"
|
||||||
|
|
||||||
|
def write(self, values):
|
||||||
|
ctx = self.env.context.copy()
|
||||||
|
if (
|
||||||
|
len(self) == 1
|
||||||
|
and "location_dest_id" in values
|
||||||
|
and self.location_dest_id.id == values.get("location_dest_id")
|
||||||
|
):
|
||||||
|
ctx.update(
|
||||||
|
{
|
||||||
|
"bypass_reservation_update": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return super(StockPackageLevel, self.with_context(**ctx)).write(values)
|
||||||
@@ -16,6 +16,8 @@ class TestRma(common.SavepointCase):
|
|||||||
cls.make_supplier_rma = cls.env["rma.order.line.make.supplier.rma"]
|
cls.make_supplier_rma = cls.env["rma.order.line.make.supplier.rma"]
|
||||||
cls.rma_add_stock_move = cls.env["rma_add_stock_move"]
|
cls.rma_add_stock_move = cls.env["rma_add_stock_move"]
|
||||||
cls.product_ctg_model = cls.env["product.category"]
|
cls.product_ctg_model = cls.env["product.category"]
|
||||||
|
cls.lot_obj = cls.env["stock.production.lot"]
|
||||||
|
cls.package_obj = cls.env["stock.quant.package"]
|
||||||
cls.stockpicking = cls.env["stock.picking"]
|
cls.stockpicking = cls.env["stock.picking"]
|
||||||
cls.rma = cls.env["rma.order"]
|
cls.rma = cls.env["rma.order"]
|
||||||
cls.rma_line = cls.env["rma.order.line"]
|
cls.rma_line = cls.env["rma.order.line"]
|
||||||
@@ -34,6 +36,8 @@ class TestRma(common.SavepointCase):
|
|||||||
)
|
)
|
||||||
cls.product_id = cls._create_product("PT0")
|
cls.product_id = cls._create_product("PT0")
|
||||||
cls.product_1 = cls._create_product("PT1")
|
cls.product_1 = cls._create_product("PT1")
|
||||||
|
cls.product_1_serial = cls._create_product("PT1 Serial", "serial")
|
||||||
|
cls.product_1_lot = cls._create_product("PT1 Lot", "lot")
|
||||||
cls.product_2 = cls._create_product("PT2")
|
cls.product_2 = cls._create_product("PT2")
|
||||||
cls.product_3 = cls._create_product("PT3")
|
cls.product_3 = cls._create_product("PT3")
|
||||||
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
||||||
@@ -151,9 +155,14 @@ class TestRma(common.SavepointCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_product(cls, name):
|
def _create_product(cls, name, tracking="none"):
|
||||||
return cls.product_product_model.create(
|
return cls.product_product_model.create(
|
||||||
{"name": name, "categ_id": cls.category.id, "type": "product"}
|
{
|
||||||
|
"name": name,
|
||||||
|
"categ_id": cls.category.id,
|
||||||
|
"type": "product",
|
||||||
|
"tracking": tracking,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -180,7 +189,7 @@ class TestRma(common.SavepointCase):
|
|||||||
picking.button_validate()
|
picking.button_validate()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _create_inventory(cls, product, qty, location):
|
def _create_inventory(cls, product, qty, location, lot_id=False, package_id=False):
|
||||||
"""
|
"""
|
||||||
Creates inventory of a product on a specific location, this will be used
|
Creates inventory of a product on a specific location, this will be used
|
||||||
eventually to create a inventory at specific cost, that will be received in
|
eventually to create a inventory at specific cost, that will be received in
|
||||||
@@ -198,6 +207,8 @@ class TestRma(common.SavepointCase):
|
|||||||
"product_uom_id": product.uom_id.id,
|
"product_uom_id": product.uom_id.id,
|
||||||
"product_qty": qty,
|
"product_qty": qty,
|
||||||
"location_id": location.id,
|
"location_id": location.id,
|
||||||
|
"prod_lot_id": lot_id,
|
||||||
|
"package_id": package_id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -245,13 +256,21 @@ class TestRma(common.SavepointCase):
|
|||||||
for item in products2move:
|
for item in products2move:
|
||||||
product = item[0]
|
product = item[0]
|
||||||
product_qty = item[1]
|
product_qty = item[1]
|
||||||
cls._create_inventory(product, product_qty, cls.stock_location)
|
lot_id = len(item) >= 3 and item[2] or False
|
||||||
|
origin_package_id = len(item) >= 4 and item[3] or False
|
||||||
|
destination_package_id = len(item) >= 5 and item[4] or False
|
||||||
|
cls._create_inventory(
|
||||||
|
product, product_qty, cls.stock_location, lot_id, origin_package_id
|
||||||
|
)
|
||||||
move_values = cls._prepare_move(
|
move_values = cls._prepare_move(
|
||||||
product,
|
product,
|
||||||
product_qty,
|
product_qty,
|
||||||
cls.stock_location,
|
cls.stock_location,
|
||||||
cls.customer_location,
|
cls.customer_location,
|
||||||
picking,
|
picking,
|
||||||
|
lot_id,
|
||||||
|
origin_package_id=origin_package_id,
|
||||||
|
destination_package_id=destination_package_id,
|
||||||
)
|
)
|
||||||
moves.append(cls.env["stock.move"].create(move_values))
|
moves.append(cls.env["stock.move"].create(move_values))
|
||||||
else:
|
else:
|
||||||
@@ -262,13 +281,21 @@ class TestRma(common.SavepointCase):
|
|||||||
for item in products2move:
|
for item in products2move:
|
||||||
product = item[0]
|
product = item[0]
|
||||||
product_qty = item[1]
|
product_qty = item[1]
|
||||||
cls._create_inventory(product, product_qty, cls.stock_location)
|
lot_id = len(item) >= 3 and item[2] or False
|
||||||
|
origin_package_id = len(item) >= 4 and item[3] or False
|
||||||
|
destination_package_id = len(item) >= 5 and item[4] or False
|
||||||
|
cls._create_inventory(
|
||||||
|
product, product_qty, cls.stock_location, lot_id, origin_package_id
|
||||||
|
)
|
||||||
move_values = cls._prepare_move(
|
move_values = cls._prepare_move(
|
||||||
product,
|
product,
|
||||||
product_qty,
|
product_qty,
|
||||||
cls.supplier_location,
|
cls.supplier_location,
|
||||||
cls.stock_rma_location,
|
cls.stock_rma_location,
|
||||||
picking,
|
picking,
|
||||||
|
lot_id,
|
||||||
|
origin_package_id=origin_package_id,
|
||||||
|
destination_package_id=destination_package_id,
|
||||||
)
|
)
|
||||||
moves.append(cls.env["stock.move"].create(move_values))
|
moves.append(cls.env["stock.move"].create(move_values))
|
||||||
# Process the picking
|
# Process the picking
|
||||||
@@ -304,7 +331,9 @@ class TestRma(common.SavepointCase):
|
|||||||
data = (
|
data = (
|
||||||
wizard.with_user(cls.rma_basic_user)
|
wizard.with_user(cls.rma_basic_user)
|
||||||
.with_context(customer=1)
|
.with_context(customer=1)
|
||||||
._prepare_rma_line_from_stock_move(move)
|
._prepare_rma_line_from_stock_move(
|
||||||
|
move, lot=len(move.lot_ids) == 1 and move.lot_ids[0] or False
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -346,10 +375,20 @@ class TestRma(common.SavepointCase):
|
|||||||
return rma_id
|
return rma_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _prepare_move(cls, product, qty, src, dest, picking_in):
|
def _prepare_move(
|
||||||
|
cls,
|
||||||
|
product,
|
||||||
|
qty,
|
||||||
|
src,
|
||||||
|
dest,
|
||||||
|
picking_in,
|
||||||
|
lot_id=False,
|
||||||
|
origin_package_id=False,
|
||||||
|
destination_package_id=False,
|
||||||
|
):
|
||||||
location_id = src.id
|
location_id = src.id
|
||||||
|
|
||||||
return {
|
res = {
|
||||||
"name": product.name,
|
"name": product.name,
|
||||||
"partner_id": picking_in.partner_id.id,
|
"partner_id": picking_in.partner_id.id,
|
||||||
"origin": picking_in.name,
|
"origin": picking_in.name,
|
||||||
@@ -363,6 +402,29 @@ class TestRma(common.SavepointCase):
|
|||||||
"picking_id": picking_in.id,
|
"picking_id": picking_in.id,
|
||||||
"price_unit": product.standard_price,
|
"price_unit": product.standard_price,
|
||||||
}
|
}
|
||||||
|
if lot_id or origin_package_id or destination_package_id:
|
||||||
|
res.update(
|
||||||
|
{
|
||||||
|
"move_line_ids": [
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"picking_id": picking_in.id,
|
||||||
|
"product_id": product.id,
|
||||||
|
"product_uom_id": product.uom_id.id,
|
||||||
|
"qty_done": qty,
|
||||||
|
"lot_id": lot_id,
|
||||||
|
"package_id": origin_package_id,
|
||||||
|
"result_package_id": destination_package_id,
|
||||||
|
"location_id": location_id,
|
||||||
|
"location_dest_id": dest.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
def _check_equal_quantity(self, qty1, qty2, msg):
|
def _check_equal_quantity(self, qty1, qty2, msg):
|
||||||
self.assertEqual(qty1, qty2, msg)
|
self.assertEqual(qty1, qty2, msg)
|
||||||
@@ -1167,3 +1229,135 @@ class TestRma(common.SavepointCase):
|
|||||||
self.assertTrue(partner, "Partner is not defined or False")
|
self.assertTrue(partner, "Partner is not defined or False")
|
||||||
moves = picking.move_lines
|
moves = picking.move_lines
|
||||||
self.assertEqual(len(moves), 1, "Incorrect number of moves created")
|
self.assertEqual(len(moves), 1, "Incorrect number of moves created")
|
||||||
|
|
||||||
|
def test_10_customer_rma_tracking_lot(self):
|
||||||
|
lot = self.lot_obj.create(
|
||||||
|
{
|
||||||
|
"product_id": self.product_1_lot.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
origin_package = self.package_obj.create({})
|
||||||
|
destination_package = self.package_obj.create({})
|
||||||
|
products2move = [
|
||||||
|
(self.product_1_lot, 5, lot.id, origin_package.id, destination_package.id)
|
||||||
|
]
|
||||||
|
rma_customer_id = self._create_rma_from_move(
|
||||||
|
products2move,
|
||||||
|
"customer",
|
||||||
|
self.env.ref("base.res_partner_2"),
|
||||||
|
dropship=False,
|
||||||
|
)
|
||||||
|
rma = rma_customer_id.rma_line_ids
|
||||||
|
rma.action_rma_to_approve()
|
||||||
|
wizard = self.rma_make_picking.with_context(
|
||||||
|
{
|
||||||
|
"active_ids": rma.ids,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"picking_type": "incoming",
|
||||||
|
"active_id": rma.ids[0],
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
wizard.action_create_picking()
|
||||||
|
res = rma.action_view_in_shipments()
|
||||||
|
self.assertTrue("res_id" in res, "Incorrect number of pickings" "created")
|
||||||
|
picking = self.env["stock.picking"].browse(res["res_id"])
|
||||||
|
self.assertEqual(len(picking), 1, "Incorrect number of pickings created")
|
||||||
|
moves = picking.move_lines
|
||||||
|
self.assertEqual(
|
||||||
|
destination_package,
|
||||||
|
moves.mapped("move_line_ids.package_id"),
|
||||||
|
"Should have same package assigned",
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
bool(moves.mapped("move_line_ids.result_package_id")),
|
||||||
|
"Destination package should not be assigned",
|
||||||
|
)
|
||||||
|
picking.action_assign()
|
||||||
|
for mv in picking.move_lines:
|
||||||
|
mv.quantity_done = mv.product_uom_qty
|
||||||
|
picking._action_done()
|
||||||
|
wizard = self.rma_make_picking.with_context(
|
||||||
|
{
|
||||||
|
"active_id": rma.ids[0],
|
||||||
|
"active_ids": rma.ids,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"picking_type": "outgoing",
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
wizard.action_create_picking()
|
||||||
|
res = rma.action_view_out_shipments()
|
||||||
|
picking = self.env["stock.picking"].browse(res["res_id"])
|
||||||
|
picking.action_assign()
|
||||||
|
for mv in picking.move_lines:
|
||||||
|
mv.quantity_done = mv.product_uom_qty
|
||||||
|
picking._action_done()
|
||||||
|
self.assertEqual(picking.state, "done", "Final picking should has done state")
|
||||||
|
|
||||||
|
def test_11_customer_rma_tracking_serial(self):
|
||||||
|
lot = self.lot_obj.create(
|
||||||
|
{
|
||||||
|
"product_id": self.product_1_serial.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
origin_package = self.package_obj.create({})
|
||||||
|
destination_package = self.package_obj.create({})
|
||||||
|
products2move = [
|
||||||
|
(
|
||||||
|
self.product_1_serial,
|
||||||
|
1,
|
||||||
|
lot.id,
|
||||||
|
origin_package.id,
|
||||||
|
destination_package.id,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
rma_customer_id = self._create_rma_from_move(
|
||||||
|
products2move,
|
||||||
|
"customer",
|
||||||
|
self.env.ref("base.res_partner_2"),
|
||||||
|
dropship=False,
|
||||||
|
)
|
||||||
|
rma = rma_customer_id.rma_line_ids
|
||||||
|
rma.action_rma_to_approve()
|
||||||
|
wizard = self.rma_make_picking.with_context(
|
||||||
|
{
|
||||||
|
"active_ids": rma.ids,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"picking_type": "incoming",
|
||||||
|
"active_id": rma.ids[0],
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
wizard.action_create_picking()
|
||||||
|
res = rma.action_view_in_shipments()
|
||||||
|
self.assertTrue("res_id" in res, "Incorrect number of pickings" "created")
|
||||||
|
picking = self.env["stock.picking"].browse(res["res_id"])
|
||||||
|
self.assertEqual(len(picking), 1, "Incorrect number of pickings created")
|
||||||
|
moves = picking.move_lines
|
||||||
|
self.assertEqual(
|
||||||
|
destination_package,
|
||||||
|
moves.mapped("move_line_ids.package_id"),
|
||||||
|
"Should have same package assigned",
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
bool(moves.mapped("move_line_ids.result_package_id")),
|
||||||
|
"Destination package should not be assigned",
|
||||||
|
)
|
||||||
|
picking.action_assign()
|
||||||
|
for mv in picking.move_lines:
|
||||||
|
mv.quantity_done = mv.product_uom_qty
|
||||||
|
picking._action_done()
|
||||||
|
wizard = self.rma_make_picking.with_context(
|
||||||
|
{
|
||||||
|
"active_id": rma.ids[0],
|
||||||
|
"active_ids": rma.ids,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"picking_type": "outgoing",
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
wizard.action_create_picking()
|
||||||
|
res = rma.action_view_out_shipments()
|
||||||
|
picking = self.env["stock.picking"].browse(res["res_id"])
|
||||||
|
picking.action_assign()
|
||||||
|
for mv in picking.move_lines:
|
||||||
|
mv.quantity_done = mv.product_uom_qty
|
||||||
|
picking._action_done()
|
||||||
|
self.assertEqual(picking.state, "done", "Final picking should has done state")
|
||||||
|
|||||||
@@ -196,6 +196,21 @@ class RmaMakePicking(models.TransientModel):
|
|||||||
procurements.extend(procurement)
|
procurements.extend(procurement)
|
||||||
return procurements
|
return procurements
|
||||||
|
|
||||||
|
def _is_final_step(self, move):
|
||||||
|
"""This function helps to know if wizard is called to finish process of rma,
|
||||||
|
customer is delivery return, and supplier is receipt return"""
|
||||||
|
if (
|
||||||
|
move.rma_line_id.type == "customer"
|
||||||
|
and self.env.context.get("picking_type") == "outgoing"
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
if (
|
||||||
|
move.rma_line_id.type == "supplier"
|
||||||
|
and self.env.context.get("picking_type") == "incoming"
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def action_create_picking(self):
|
def action_create_picking(self):
|
||||||
self._create_picking()
|
self._create_picking()
|
||||||
move_line_model = self.env["stock.move.line"]
|
move_line_model = self.env["stock.move.line"]
|
||||||
@@ -215,23 +230,68 @@ class RmaMakePicking(models.TransientModel):
|
|||||||
and x.rma_line_id.lot_id
|
and x.rma_line_id.lot_id
|
||||||
):
|
):
|
||||||
# Force the reservation of the RMA specific lot for incoming shipments.
|
# Force the reservation of the RMA specific lot for incoming shipments.
|
||||||
|
is_final_step = self._is_final_step(move)
|
||||||
move.move_line_ids.unlink()
|
move.move_line_ids.unlink()
|
||||||
|
reference_moves = (
|
||||||
|
not is_final_step
|
||||||
|
and move.rma_line_id._get_stock_move_reference()
|
||||||
|
or self.env["stock.move"]
|
||||||
|
)
|
||||||
|
package = reference_moves.mapped("move_line_ids.result_package_id")
|
||||||
|
quants = self.env["stock.quant"]._gather(
|
||||||
|
move.product_id,
|
||||||
|
move.location_id,
|
||||||
|
lot_id=move.rma_line_id.lot_id,
|
||||||
|
package_id=len(package) == 1 and package or False,
|
||||||
|
)
|
||||||
|
move_line_data = move._prepare_move_line_vals(
|
||||||
|
reserved_quant=(len(quants) == 1) and quants or False
|
||||||
|
)
|
||||||
|
move_line_data.update(
|
||||||
|
{
|
||||||
|
"qty_done": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if move.rma_line_id.lot_id and not quants:
|
||||||
|
# CHECK ME: force al least has lot assigned if quant is not found
|
||||||
|
move_line_data.update(
|
||||||
|
{
|
||||||
|
"lot_id": move.rma_line_id.lot_id.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
if move.product_id.tracking == "serial":
|
if move.product_id.tracking == "serial":
|
||||||
move.write(
|
move.write(
|
||||||
{
|
{
|
||||||
"lot_ids": [(6, 0, move.rma_line_id.lot_id.ids)],
|
"lot_ids": move.rma_line_id.lot_id.ids,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
quants = self.env["stock.quant"]._gather(
|
move_line_data.update(
|
||||||
move.product_id, move.location_id, lot_id=move.rma_line_id.lot_id
|
|
||||||
)
|
|
||||||
move.move_line_ids.write(
|
|
||||||
{
|
{
|
||||||
"product_uom_qty": 1 if picking_type == "incoming" else 0,
|
"product_uom_qty": 1.0,
|
||||||
"qty_done": 0,
|
|
||||||
"package_id": len(quants) == 1 and quants.package_id.id,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if move.move_line_ids:
|
||||||
|
move.move_line_ids.with_context(
|
||||||
|
bypass_reservation_update=True
|
||||||
|
).write(
|
||||||
|
{
|
||||||
|
"lot_id": move_line_data.get("lot_id"),
|
||||||
|
"package_id": move_line_data.get("package_id"),
|
||||||
|
"result_package_id": move_line_data.get(
|
||||||
|
"result_package_id", False
|
||||||
|
),
|
||||||
|
"product_uom_qty": 1.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
len(quants) == 1
|
||||||
|
and quants.reserved_quantity == 0
|
||||||
|
and quants.quantity == 1
|
||||||
|
and quants.location_id.usage not in ("customer", "supplier")
|
||||||
|
):
|
||||||
|
quants.sudo().write(
|
||||||
|
{"reserved_quantity": quants.reserved_quantity + 1}
|
||||||
|
)
|
||||||
elif move.product_id.tracking == "lot":
|
elif move.product_id.tracking == "lot":
|
||||||
if picking_type == "incoming":
|
if picking_type == "incoming":
|
||||||
qty = self.item_ids.filtered(
|
qty = self.item_ids.filtered(
|
||||||
@@ -241,15 +301,12 @@ class RmaMakePicking(models.TransientModel):
|
|||||||
qty = self.item_ids.filtered(
|
qty = self.item_ids.filtered(
|
||||||
lambda x: x.line_id.id == move.rma_line_id.id
|
lambda x: x.line_id.id == move.rma_line_id.id
|
||||||
).qty_to_deliver
|
).qty_to_deliver
|
||||||
move_line_data = move._prepare_move_line_vals()
|
|
||||||
move_line_data.update(
|
move_line_data.update(
|
||||||
{
|
{
|
||||||
"lot_id": move.rma_line_id.lot_id.id,
|
|
||||||
"product_uom_id": move.product_id.uom_id.id,
|
|
||||||
"qty_done": 0,
|
|
||||||
"product_uom_qty": qty if picking_type == "incoming" else 0,
|
"product_uom_qty": qty if picking_type == "incoming" else 0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if not move.move_line_ids:
|
||||||
move_line_model.create(move_line_data)
|
move_line_model.create(move_line_data)
|
||||||
pickings.with_context(force_no_bypass_reservation=True).action_assign()
|
pickings.with_context(force_no_bypass_reservation=True).action_assign()
|
||||||
return action
|
return action
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ class RmaOrderLine(models.Model):
|
|||||||
comodel_name="sale.order.line", compute="_compute_sale_line_domain"
|
comodel_name="sale.order.line", compute="_compute_sale_line_domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _get_stock_move_reference(self):
|
||||||
|
self.ensure_one()
|
||||||
|
move = self.reference_move_id
|
||||||
|
if self.sale_line_id:
|
||||||
|
# CHECK ME: backorder cases can be more than one move
|
||||||
|
sale_moves = self.sale_line_id.move_ids.filtered(
|
||||||
|
lambda x: x.location_dest_id.usage == "customer" and x.state == "done"
|
||||||
|
)
|
||||||
|
if sale_moves:
|
||||||
|
return sale_moves
|
||||||
|
return move
|
||||||
|
|
||||||
@api.depends("product_id", "partner_id")
|
@api.depends("product_id", "partner_id")
|
||||||
def _compute_sale_line_domain(self):
|
def _compute_sale_line_domain(self):
|
||||||
line_model = self.env["sale.order.line"]
|
line_model = self.env["sale.order.line"]
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from . import test_rma_sale
|
from . import test_rma_sale
|
||||||
from . import test_rma_stock_account_sale
|
from . import test_rma_stock_account_sale
|
||||||
|
from . import test_rma_sale_tracking
|
||||||
|
|||||||
153
rma_sale/tests/test_rma_sale_tracking.py
Normal file
153
rma_sale/tests/test_rma_sale_tracking.py
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
from odoo.addons.rma.tests.test_rma import TestRma
|
||||||
|
|
||||||
|
|
||||||
|
class TestRmaSaleTracking(TestRma):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestRmaSaleTracking, cls).setUpClass()
|
||||||
|
cls.rma_obj = cls.env["rma.order"]
|
||||||
|
cls.rma_line_obj = cls.env["rma.order.line"]
|
||||||
|
cls.rma_op_obj = cls.env["rma.operation"]
|
||||||
|
cls.rma_add_sale_wiz = cls.env["rma_add_sale"]
|
||||||
|
cls.rma_make_sale_wiz = cls.env["rma.order.line.make.sale.order"]
|
||||||
|
cls.so_obj = cls.env["sale.order"]
|
||||||
|
cls.sol_obj = cls.env["sale.order.line"]
|
||||||
|
cls.product_obj = cls.env["product.product"]
|
||||||
|
cls.partner_obj = cls.env["res.partner"]
|
||||||
|
cls.rma_route_cust = cls.env.ref("rma.route_rma_customer")
|
||||||
|
cls.customer1 = cls.partner_obj.create({"name": "Customer 1"})
|
||||||
|
cls.product_lot_2 = cls._create_product("PT2 Lot", "lot")
|
||||||
|
cls.product_serial_2 = cls._create_product("PT2 Serial", "serial")
|
||||||
|
|
||||||
|
cls.so = cls.so_obj.create(
|
||||||
|
{
|
||||||
|
"partner_id": cls.customer1.id,
|
||||||
|
"partner_invoice_id": cls.customer1.id,
|
||||||
|
"partner_shipping_id": cls.customer1.id,
|
||||||
|
"order_line": [
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"name": cls.product_serial_2.name,
|
||||||
|
"product_id": cls.product_serial_2.id,
|
||||||
|
"product_uom_qty": 1.0,
|
||||||
|
"product_uom": cls.product_serial_2.uom_id.id,
|
||||||
|
"price_unit": cls.product_serial_2.list_price,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"name": cls.product_lot_2.name,
|
||||||
|
"product_id": cls.product_lot_2.id,
|
||||||
|
"product_uom_qty": 18.0,
|
||||||
|
"product_uom": cls.product_lot_2.uom_id.id,
|
||||||
|
"price_unit": cls.product_lot_2.list_price,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
"pricelist_id": cls.env.ref("product.list0").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cls.so.action_confirm()
|
||||||
|
|
||||||
|
cls.serial = cls.lot_obj.create(
|
||||||
|
{
|
||||||
|
"product_id": cls.product_serial_2.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cls.lot = cls.lot_obj.create(
|
||||||
|
{
|
||||||
|
"product_id": cls.product_lot_2.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.package_1 = cls.package_obj.create({})
|
||||||
|
cls.package_2 = cls.package_obj.create({})
|
||||||
|
cls.package_3 = cls.package_obj.create({})
|
||||||
|
cls.package_4 = cls.package_obj.create({})
|
||||||
|
|
||||||
|
cls._create_inventory(
|
||||||
|
cls.product_serial_2, 1, cls.stock_location, cls.serial.id, cls.package_1.id
|
||||||
|
)
|
||||||
|
|
||||||
|
cls._create_inventory(
|
||||||
|
cls.product_lot_2, 1, cls.stock_location, cls.lot.id, cls.package_3.id
|
||||||
|
)
|
||||||
|
|
||||||
|
picking = cls.so.picking_ids
|
||||||
|
|
||||||
|
picking.action_assign()
|
||||||
|
|
||||||
|
for move in picking.move_lines:
|
||||||
|
if move.product_id.id == cls.product_serial_2.id:
|
||||||
|
move.move_line_ids.write({"result_package_id": cls.package_2.id})
|
||||||
|
if move.product_id.id == cls.product_lot_2.id:
|
||||||
|
move.move_line_ids.write({"result_package_id": cls.package_4.id})
|
||||||
|
|
||||||
|
cls._do_picking(picking)
|
||||||
|
|
||||||
|
# Create RMA group and operation:
|
||||||
|
cls.rma_group = cls.rma_obj.create({"partner_id": cls.customer1.id})
|
||||||
|
cls.operation_1 = cls.rma_op_obj.create(
|
||||||
|
{
|
||||||
|
"code": "TEST",
|
||||||
|
"name": "Sale afer receive",
|
||||||
|
"type": "customer",
|
||||||
|
"receipt_policy": "ordered",
|
||||||
|
"sale_policy": "received",
|
||||||
|
"in_route_id": cls.rma_route_cust.id,
|
||||||
|
"out_route_id": cls.rma_route_cust.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_sale = cls.rma_add_sale_wiz.with_context(
|
||||||
|
{
|
||||||
|
"customer": True,
|
||||||
|
"active_ids": cls.rma_group.id,
|
||||||
|
"active_model": "rma.order",
|
||||||
|
}
|
||||||
|
).create(
|
||||||
|
{"sale_id": cls.so.id, "sale_line_ids": [(6, 0, cls.so.order_line.ids)]}
|
||||||
|
)
|
||||||
|
add_sale.add_lines()
|
||||||
|
|
||||||
|
def test_01_customer_rma_tracking(self):
|
||||||
|
rma_serial = self.rma_group.rma_line_ids.filtered(
|
||||||
|
lambda r: r.product_id == self.product_serial_2
|
||||||
|
)
|
||||||
|
rma_lot = self.rma_group.rma_line_ids.filtered(
|
||||||
|
lambda r: r.product_id == self.product_lot_2
|
||||||
|
)
|
||||||
|
for rma in rma_serial + rma_lot:
|
||||||
|
wizard = self.rma_make_picking.with_context(
|
||||||
|
{
|
||||||
|
"active_ids": rma.ids,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"picking_type": "incoming",
|
||||||
|
"active_id": rma.ids[0],
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
wizard.action_create_picking()
|
||||||
|
res = rma.action_view_in_shipments()
|
||||||
|
self.assertTrue("res_id" in res, "Incorrect number of pickings" "created")
|
||||||
|
picking = self.env["stock.picking"].browse(res["res_id"])
|
||||||
|
self.assertEqual(len(picking), 1, "Incorrect number of pickings created")
|
||||||
|
moves = picking.move_lines
|
||||||
|
self.asserTrue(
|
||||||
|
bool(moves.mapped("move_line_ids.package_id")),
|
||||||
|
"Should have same package assigned",
|
||||||
|
)
|
||||||
|
self.assertFalse(
|
||||||
|
bool(moves.mapped("move_line_ids.result_package_id")),
|
||||||
|
"Destination package should not be assigned",
|
||||||
|
)
|
||||||
|
picking.action_assign()
|
||||||
|
for mv in picking.move_lines:
|
||||||
|
mv.quantity_done = mv.product_uom_qty
|
||||||
|
picking._action_done()
|
||||||
|
self.assertEqual(
|
||||||
|
picking.state, "done", "Final picking should has done state"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user