mirror of
https://github.com/ForgeFlow/stock-rma.git
synced 2025-01-21 12:57:49 +02:00
[14.0][ADD] rma_scrap
This commit is contained in:
committed by
JasminSForgeFlow
parent
14401983a8
commit
a98271f31a
45
rma_scrap/README.rst
Normal file
45
rma_scrap/README.rst
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||||
|
:alt: License LGPL-3
|
||||||
|
|
||||||
|
============
|
||||||
|
RMA Scrap
|
||||||
|
============
|
||||||
|
|
||||||
|
This module allows you to put scrap the products.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
Go to *RMA / Configuration / Customer Operations* and define there:
|
||||||
|
|
||||||
|
#. The Scrap Policy
|
||||||
|
#. The default scrap destination location .
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
#. Go to a Customer RMA.
|
||||||
|
#. Click on *Scrap*.
|
||||||
|
#. Indicate the quantity that you want to scrap.
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues
|
||||||
|
<https://github.com/Eficent/stock-rma/issues>`_. In case of trouble, please
|
||||||
|
check there if your issue has already been reported. If you spotted it first,
|
||||||
|
help us smashing it by providing a detailed and welcomed feedback.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* David Jimenez <david.jimenez@ForgeFlow.com>
|
||||||
|
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
This module is maintained by ForgeFlow
|
||||||
2
rma_scrap/__init__.py
Normal file
2
rma_scrap/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from . import wizards
|
||||||
|
from . import models
|
||||||
18
rma_scrap/__manifest__.py
Normal file
18
rma_scrap/__manifest__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "RMA Put Away",
|
||||||
|
"version": "14.0.1.0.0",
|
||||||
|
"license": "LGPL-3",
|
||||||
|
"category": "RMA",
|
||||||
|
"summary": "Allows to put away the received products in odoo",
|
||||||
|
"author": "ForgeFlow",
|
||||||
|
"website": "https://github.com/ForgeFlow/stock-rma",
|
||||||
|
"depends": ["rma"],
|
||||||
|
"data": [
|
||||||
|
"security/ir.model.access.csv",
|
||||||
|
"views/rma_operation_view.xml",
|
||||||
|
"views/rma_order_line_view.xml",
|
||||||
|
"views/rma_order_view.xml",
|
||||||
|
"wizards/rma_scrap_view.xml",
|
||||||
|
],
|
||||||
|
"installable": True,
|
||||||
|
}
|
||||||
6
rma_scrap/models/__init__.py
Normal file
6
rma_scrap/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from . import rma_operation
|
||||||
|
from . import stock_move
|
||||||
|
from . import stock_rule
|
||||||
|
from . import rma_order_line
|
||||||
|
from . import rma_order
|
||||||
|
from . import stock_scrap
|
||||||
24
rma_scrap/models/rma_operation.py
Normal file
24
rma_scrap/models/rma_operation.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class RmaOperation(models.Model):
|
||||||
|
_inherit = "rma.operation"
|
||||||
|
|
||||||
|
scrap_policy = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
("no", "Not required"),
|
||||||
|
("ordered", "Based on Ordered Quantities"),
|
||||||
|
("received", "Based on Received Quantities"),
|
||||||
|
],
|
||||||
|
string="Scrap Policy",
|
||||||
|
default="no",
|
||||||
|
)
|
||||||
|
|
||||||
|
scrap_location_id = fields.Many2one(
|
||||||
|
comodel_name="stock.location",
|
||||||
|
string="Scrap Destination Location",
|
||||||
|
domain="[('scrap_location', '=', True), ('company_id', 'in', [company_id, False])]",
|
||||||
|
)
|
||||||
31
rma_scrap/models/rma_order.py
Normal file
31
rma_scrap/models/rma_order.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class RmaOrder(models.Model):
|
||||||
|
_inherit = "rma.order"
|
||||||
|
|
||||||
|
def _compute_scrap_count(self):
|
||||||
|
for order in self:
|
||||||
|
moves = (
|
||||||
|
order.mapped("rma_line_ids.move_ids")
|
||||||
|
.filtered(lambda m: m.is_rma_scrap)
|
||||||
|
.move_line_ids
|
||||||
|
)
|
||||||
|
order.scrap_count = len(moves)
|
||||||
|
|
||||||
|
scrap_count = fields.Integer(compute="_compute_scrap_count", string="# Scrap")
|
||||||
|
|
||||||
|
def action_view_scrap_transfers(self):
|
||||||
|
self.ensure_one()
|
||||||
|
action = self.env.ref("stock.action_stock_scrap")
|
||||||
|
result = action.sudo().read()[0]
|
||||||
|
scraps = self.env["stock.scrap"].search([("origin", "=", self.name)])
|
||||||
|
if len(scraps) > 1:
|
||||||
|
result["domain"] = [("id", "in", scraps.ids)]
|
||||||
|
elif len(scraps) == 1:
|
||||||
|
res = self.env.ref("stock.stock_scrap_form_view", False)
|
||||||
|
result["views"] = [(res and res.id or False, "form")]
|
||||||
|
result["res_id"] = scraps.ids[0]
|
||||||
|
return result
|
||||||
106
rma_scrap/models/rma_order_line.py
Normal file
106
rma_scrap/models/rma_order_line.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class RmaOrderLine(models.Model):
|
||||||
|
_inherit = "rma.order.line"
|
||||||
|
|
||||||
|
@api.depends(
|
||||||
|
"move_ids",
|
||||||
|
"move_ids.state",
|
||||||
|
"move_ids.is_rma_scrap",
|
||||||
|
"qty_scrap",
|
||||||
|
"scrap_policy",
|
||||||
|
"product_qty",
|
||||||
|
)
|
||||||
|
def _compute_qty_to_scrap(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.qty_to_scrap = 0.0
|
||||||
|
if rec.scrap_policy == "ordered":
|
||||||
|
rec.qty_to_scrap = rec.product_qty - rec.qty_scrap
|
||||||
|
elif rec.scrap_policy == "received":
|
||||||
|
rec.qty_to_scrap = rec.qty_received - rec.qty_scrap
|
||||||
|
|
||||||
|
@api.depends("move_ids", "move_ids.state", "move_ids.is_rma_scrap")
|
||||||
|
def _compute_qty_in_scrap(self):
|
||||||
|
product_obj = self.env["uom.uom"]
|
||||||
|
for rec in self:
|
||||||
|
qty = 0.0
|
||||||
|
for move in self.env["stock.scrap"].search(
|
||||||
|
[("origin", "=", rec.name), ("state", "=", "draft")]
|
||||||
|
):
|
||||||
|
qty += product_obj._compute_quantity(move.scrap_qty, rec.uom_id)
|
||||||
|
rec.qty_in_scrap = qty
|
||||||
|
|
||||||
|
@api.depends("move_ids", "move_ids.state", "move_ids.is_rma_scrap")
|
||||||
|
def _compute_qty_scrap(self):
|
||||||
|
product_obj = self.env["uom.uom"]
|
||||||
|
for rec in self:
|
||||||
|
qty = 0.0
|
||||||
|
for move in rec.move_ids.filtered(
|
||||||
|
lambda m: m.state in ["done"] and m.is_rma_scrap
|
||||||
|
):
|
||||||
|
qty += product_obj._compute_quantity(move.product_uom_qty, rec.uom_id)
|
||||||
|
rec.qty_scrap = qty
|
||||||
|
|
||||||
|
def _compute_scrap_count(self):
|
||||||
|
for line in self:
|
||||||
|
scraps = self.env["stock.scrap"].search([("origin", "=", line.name)])
|
||||||
|
line.scrap_count = len(scraps)
|
||||||
|
|
||||||
|
qty_to_scrap = fields.Float(
|
||||||
|
string="Qty To Scrap",
|
||||||
|
copy=False,
|
||||||
|
digits="Product Unit of Measure",
|
||||||
|
readonly=True,
|
||||||
|
compute="_compute_qty_to_scrap",
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
qty_in_scrap = fields.Float(
|
||||||
|
string="Qty In Scrap",
|
||||||
|
copy=False,
|
||||||
|
digits="Product Unit of Measure",
|
||||||
|
readonly=True,
|
||||||
|
compute="_compute_qty_in_scrap",
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
qty_scrap = fields.Float(
|
||||||
|
string="Qty Scrap",
|
||||||
|
copy=False,
|
||||||
|
digits="Product Unit of Measure",
|
||||||
|
readonly=True,
|
||||||
|
compute="_compute_qty_scrap",
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
scrap_policy = fields.Selection(
|
||||||
|
selection=[
|
||||||
|
("no", "Not required"),
|
||||||
|
("ordered", "Based on Ordered Quantities"),
|
||||||
|
("received", "Based on Received Quantities"),
|
||||||
|
],
|
||||||
|
string="Scrap Policy",
|
||||||
|
default="no",
|
||||||
|
required=True,
|
||||||
|
readonly=False,
|
||||||
|
)
|
||||||
|
scrap_count = fields.Integer(compute="_compute_scrap_count", string="# Scraps")
|
||||||
|
|
||||||
|
@api.onchange("operation_id")
|
||||||
|
def _onchange_operation_id(self):
|
||||||
|
res = super(RmaOrderLine, self)._onchange_operation_id()
|
||||||
|
if self.operation_id:
|
||||||
|
self.scrap_policy = self.operation_id.scrap_policy or "no"
|
||||||
|
return res
|
||||||
|
|
||||||
|
def action_view_scrap_transfers(self):
|
||||||
|
action = self.env.ref("stock.action_stock_scrap")
|
||||||
|
result = action.sudo().read()[0]
|
||||||
|
scraps = self.env["stock.scrap"].search([("origin", "=", self.name)])
|
||||||
|
if len(scraps) > 1:
|
||||||
|
result["domain"] = [("id", "in", scraps.ids)]
|
||||||
|
elif len(scraps) == 1:
|
||||||
|
res = self.env.ref("stock.stock_scrap_form_view", False)
|
||||||
|
result["views"] = [(res and res.id or False, "form")]
|
||||||
|
result["res_id"] = scraps.ids[0]
|
||||||
|
return result
|
||||||
20
rma_scrap/models/stock_move.py
Normal file
20
rma_scrap/models/stock_move.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class StockMove(models.Model):
|
||||||
|
_inherit = "stock.move"
|
||||||
|
|
||||||
|
is_rma_scrap = fields.Boolean(
|
||||||
|
string="Is RMA Scrap",
|
||||||
|
copy=False,
|
||||||
|
help="This Stock Move has been created from a Scrap operation in " "the RMA.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _is_in_out_rma_move(self, op, states, location_type):
|
||||||
|
res = super(StockMove, self)._is_in_out_rma_move(op, states, location_type)
|
||||||
|
if self.is_rma_scrap:
|
||||||
|
return False
|
||||||
|
return res
|
||||||
33
rma_scrap/models/stock_rule.py
Normal file
33
rma_scrap/models/stock_rule.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||||
|
|
||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class StockRule(models.Model):
|
||||||
|
_inherit = "stock.rule"
|
||||||
|
|
||||||
|
def _get_stock_move_values(
|
||||||
|
self,
|
||||||
|
product_id,
|
||||||
|
product_qty,
|
||||||
|
product_uom,
|
||||||
|
location_id,
|
||||||
|
name,
|
||||||
|
origin,
|
||||||
|
company_id,
|
||||||
|
values,
|
||||||
|
):
|
||||||
|
res = super()._get_stock_move_values(
|
||||||
|
product_id,
|
||||||
|
product_qty,
|
||||||
|
product_uom,
|
||||||
|
location_id,
|
||||||
|
name,
|
||||||
|
origin,
|
||||||
|
company_id,
|
||||||
|
values,
|
||||||
|
)
|
||||||
|
if "is_rma_scrap" in values:
|
||||||
|
res["is_rma_scrap"] = values.get("is_rma_scrap")
|
||||||
|
return res
|
||||||
23
rma_scrap/models/stock_scrap.py
Normal file
23
rma_scrap/models/stock_scrap.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class StockScrap(models.Model):
|
||||||
|
_inherit = "stock.scrap"
|
||||||
|
|
||||||
|
rma_line_id = fields.Many2one("rma.order.line", string="RMA order Line")
|
||||||
|
|
||||||
|
is_rma_scrap = fields.Boolean(
|
||||||
|
string="Is RMA Scrap",
|
||||||
|
default=False,
|
||||||
|
copy=False,
|
||||||
|
help="This Stock Move has been created from a Scrap operation in " "the RMA.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def do_scrap(self):
|
||||||
|
super(StockScrap, self).do_scrap()
|
||||||
|
if self.is_rma_scrap:
|
||||||
|
self.move_id.is_rma_scrap = True
|
||||||
|
self.rma_line_id.move_ids |= self.move_id
|
||||||
7
rma_scrap/security/ir.model.access.csv
Normal file
7
rma_scrap/security/ir.model.access.csv
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_rma_scrap_wizard,rma.order.manager,model_rma_make_scrap_wizard,rma.group_rma_manager,1,1,1,1
|
||||||
|
access_rma_scrap_wizard_customer,rma.order.manager,model_rma_make_scrap_wizard,rma.group_rma_customer_user,1,1,1,1
|
||||||
|
access_rma_scrap_wizard_supplier,rma.order.manager,model_rma_make_scrap_wizard,rma.group_rma_supplier_user,1,1,1,1
|
||||||
|
access_rma_scrap_item_wizard,rma.order.manager,model_rma_make_scrap_item_wizard,rma.group_rma_manager,1,1,1,1
|
||||||
|
access_rma_scrap_item_wizard_customer,rma.order.manager,model_rma_make_scrap_item_wizard,rma.group_rma_customer_user,1,1,1,1
|
||||||
|
access_rma_scrap_item_wizard_supplier,rma.order.manager,model_rma_make_scrap_item_wizard,rma.group_rma_supplier_user,1,1,1,1
|
||||||
|
1
rma_scrap/tests/__init__.py
Normal file
1
rma_scrap/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import test_rma_scrap
|
||||||
172
rma_scrap/tests/test_rma_scrap.py
Normal file
172
rma_scrap/tests/test_rma_scrap.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
from odoo.tests import common
|
||||||
|
|
||||||
|
|
||||||
|
class TestRmaScrap(common.SingleTransactionCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(TestRmaScrap, 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_make_picking = cls.env["rma_make_picking.wizard"]
|
||||||
|
cls.rma_make_scrap_wiz = cls.env["rma_make_scrap.wizard"]
|
||||||
|
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.cust_loc = cls.env.ref("stock.stock_location_customers")
|
||||||
|
|
||||||
|
# Create customer
|
||||||
|
cls.customer1 = cls.partner_obj.create({"name": "Customer 1"})
|
||||||
|
|
||||||
|
# Create products
|
||||||
|
cls.product_1 = cls.product_obj.create(
|
||||||
|
{"name": "Test Product 1", "type": "product", "list_price": 100.0}
|
||||||
|
)
|
||||||
|
cls.product_2 = cls.product_obj.create(
|
||||||
|
{
|
||||||
|
"name": "Test Product 2",
|
||||||
|
"type": "product",
|
||||||
|
"list_price": 150.0,
|
||||||
|
"tracking": "lot",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
cls.lot = cls.env["stock.production.lot"].create(
|
||||||
|
{
|
||||||
|
"name": "Lot for tests",
|
||||||
|
"product_id": cls.product_2.id,
|
||||||
|
"company_id": cls.env.ref("base.main_company").id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cls.wh = cls.env.ref("stock.warehouse0")
|
||||||
|
cls.stock_rma_location = cls.wh.lot_rma_id
|
||||||
|
cls.scrap_loc = cls.env["stock.location"].create(
|
||||||
|
{
|
||||||
|
"name": "WH Scrap Location",
|
||||||
|
"location_id": cls.wh.view_location_id.id,
|
||||||
|
"scrap_location": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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": "TEST1",
|
||||||
|
"name": "Operation 1",
|
||||||
|
"type": "customer",
|
||||||
|
"receipt_policy": "ordered",
|
||||||
|
"scrap_policy": "received",
|
||||||
|
"scrap_location_id": cls.scrap_loc.id,
|
||||||
|
"in_route_id": cls.rma_route_cust.id,
|
||||||
|
"out_route_id": cls.rma_route_cust.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cls.operation_2 = cls.rma_op_obj.create(
|
||||||
|
{
|
||||||
|
"code": "TEST2",
|
||||||
|
"name": "Operation 2",
|
||||||
|
"type": "customer",
|
||||||
|
"receipt_policy": "ordered",
|
||||||
|
"scrap_policy": "ordered",
|
||||||
|
"scrap_location_id": cls.scrap_loc.id,
|
||||||
|
"in_route_id": cls.rma_route_cust.id,
|
||||||
|
"out_route_id": cls.rma_route_cust.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_01_rma_scrap_received(self):
|
||||||
|
rma = self.rma_line_obj.create(
|
||||||
|
{
|
||||||
|
"partner_id": self.customer1.id,
|
||||||
|
"product_id": self.product_1.id,
|
||||||
|
"operation_id": self.operation_1.id,
|
||||||
|
"uom_id": self.product_1.uom_id.id,
|
||||||
|
"in_route_id": self.operation_1.in_route_id.id,
|
||||||
|
"out_route_id": self.operation_1.out_route_id.id,
|
||||||
|
"in_warehouse_id": self.operation_1.in_warehouse_id.id,
|
||||||
|
"out_warehouse_id": self.operation_1.out_warehouse_id.id,
|
||||||
|
"location_id": self.stock_rma_location.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
rma._onchange_operation_id()
|
||||||
|
rma.action_rma_to_approve()
|
||||||
|
wizard = self.rma_make_picking.with_context(
|
||||||
|
{
|
||||||
|
"active_ids": rma.id,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"picking_type": "incoming",
|
||||||
|
"active_id": 1,
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
wizard._create_picking()
|
||||||
|
rma._compute_qty_to_scrap()
|
||||||
|
wizard = self.rma_make_scrap_wiz.with_context(
|
||||||
|
{
|
||||||
|
"active_ids": rma.id,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"item_ids": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"line_id": rma.id,
|
||||||
|
"product_id": rma.product_id.id,
|
||||||
|
"product_qty": rma.product_qty,
|
||||||
|
"location_id": rma.location_id,
|
||||||
|
"qty_to_scrap": rma.qty_to_scrap,
|
||||||
|
"uom_id": rma.uom_id.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
action = wizard.action_create_scrap()
|
||||||
|
scrap = self.env["stock.scrap"].browse([action["res_id"]])
|
||||||
|
self.assertEqual(scrap.location_id.id, self.stock_rma_location.id)
|
||||||
|
self.assertEqual(scrap.move_id.id, False)
|
||||||
|
scrap.action_validate()
|
||||||
|
move = scrap.move_id
|
||||||
|
self.assertEqual(move.product_id.id, self.product_1.id)
|
||||||
|
|
||||||
|
def test_02_rma_scrap_ordered(self):
|
||||||
|
rma = self.rma_line_obj.create(
|
||||||
|
{
|
||||||
|
"partner_id": self.customer1.id,
|
||||||
|
"product_id": self.product_1.id,
|
||||||
|
"operation_id": self.operation_2.id,
|
||||||
|
"uom_id": self.product_1.uom_id.id,
|
||||||
|
"in_route_id": self.operation_2.in_route_id.id,
|
||||||
|
"out_route_id": self.operation_2.out_route_id.id,
|
||||||
|
"in_warehouse_id": self.operation_2.in_warehouse_id.id,
|
||||||
|
"out_warehouse_id": self.operation_2.out_warehouse_id.id,
|
||||||
|
"location_id": self.stock_rma_location.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
rma._onchange_operation_id()
|
||||||
|
rma.action_rma_to_approve()
|
||||||
|
rma._compute_qty_to_scrap()
|
||||||
|
wizard = self.rma_make_scrap_wiz.with_context(
|
||||||
|
{
|
||||||
|
"active_ids": rma.id,
|
||||||
|
"active_model": "rma.order.line",
|
||||||
|
"item_ids": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
"line_id": rma.id,
|
||||||
|
"product_id": rma.product_id.id,
|
||||||
|
"product_qty": rma.product_qty,
|
||||||
|
"location_id": rma.location_id,
|
||||||
|
"qty_to_scrap": rma.qty_to_scrap,
|
||||||
|
"uom_id": rma.uom_id.id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
).create({})
|
||||||
|
action = wizard.action_create_scrap()
|
||||||
|
scrap = self.env["stock.scrap"].browse([action["res_id"]])
|
||||||
|
self.assertEqual(scrap.location_id.id, self.stock_rma_location.id)
|
||||||
|
self.assertEqual(scrap.move_id.id, False)
|
||||||
|
self.assertTrue(scrap.action_validate())
|
||||||
34
rma_scrap/views/rma_operation_view.xml
Normal file
34
rma_scrap/views/rma_operation_view.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="rma_operation_tree" model="ir.ui.view">
|
||||||
|
<field name="name">rma.operation.tree</field>
|
||||||
|
<field name="model">rma.operation</field>
|
||||||
|
<field name="inherit_id" ref="rma.rma_operation_tree" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="delivery_policy" position="after">
|
||||||
|
<field name="scrap_policy" />
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="rma_operation_form" model="ir.ui.view">
|
||||||
|
<field name="name">rma.operation.form - rma_scrap</field>
|
||||||
|
<field name="model">rma.operation</field>
|
||||||
|
<field name="inherit_id" ref="rma.rma_operation_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="delivery_policy" position="after">
|
||||||
|
<field name="scrap_policy" />
|
||||||
|
</field>
|
||||||
|
<group name="outbound" position="after">
|
||||||
|
<group name="scrap" string="Scrap">
|
||||||
|
<field
|
||||||
|
name="scrap_location_id"
|
||||||
|
string="Destination Scrap Location"
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
51
rma_scrap/views/rma_order_line_view.xml
Normal file
51
rma_scrap/views/rma_order_line_view.xml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="view_rma_line_form" model="ir.ui.view">
|
||||||
|
<field name="name">rma.order.line.form</field>
|
||||||
|
<field name="model">rma.order.line</field>
|
||||||
|
<field name="inherit_id" ref="rma.view_rma_line_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<button name='action_view_out_shipments' position="after">
|
||||||
|
<button
|
||||||
|
type="object"
|
||||||
|
name="action_view_scrap_transfers"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-truck"
|
||||||
|
groups="stock.group_stock_user"
|
||||||
|
attrs="{'invisible':['|', ('scrap_count', '=', 0), ('state', '=', 'draft')]}"
|
||||||
|
>
|
||||||
|
<field name="scrap_count" widget="statinfo" string="Scraps" />
|
||||||
|
</button>
|
||||||
|
</button>
|
||||||
|
<group name="quantities" position="inside">
|
||||||
|
<group>
|
||||||
|
<field name="qty_to_scrap" />
|
||||||
|
<field name="qty_in_scrap" />
|
||||||
|
<field name="qty_scrap" />
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<field name="delivery_policy" position="after">
|
||||||
|
<field name="scrap_policy" />
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_rma_rma_line_filter" model="ir.ui.view">
|
||||||
|
<field name="name">rma.order.line.select - rma_scrap</field>
|
||||||
|
<field name="model">rma.order.line</field>
|
||||||
|
<field name="inherit_id" ref="rma.view_rma_rma_line_filter" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<group name="stock_quantities" position="after">
|
||||||
|
<group name="scrap_quantities" groups="stock.group_stock_user">
|
||||||
|
<filter
|
||||||
|
domain="[('state','!=', 'done'),('qty_to_scrap','>',0.0)]"
|
||||||
|
help="To Scrap"
|
||||||
|
name="to_scrap"
|
||||||
|
/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
25
rma_scrap/views/rma_order_view.xml
Normal file
25
rma_scrap/views/rma_order_view.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_rma_form" model="ir.ui.view">
|
||||||
|
<field name="name">rma.order.form</field>
|
||||||
|
<field name="model">rma.order</field>
|
||||||
|
<field name="inherit_id" ref="rma.view_rma_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<button name="action_view_out_shipments" position="after">
|
||||||
|
<button
|
||||||
|
type="object"
|
||||||
|
name="action_view_scrap_transfers"
|
||||||
|
class="oe_stat_button"
|
||||||
|
icon="fa-truck"
|
||||||
|
groups="stock.group_stock_user"
|
||||||
|
attrs="{'invisible': [('scrap_count', '=', 0)]}"
|
||||||
|
>
|
||||||
|
<field name="scrap_count" widget="statinfo" string="Scraps" />
|
||||||
|
</button>
|
||||||
|
</button>
|
||||||
|
<xpath expr="//field[@name='rma_line_ids']/tree" position="inside">
|
||||||
|
<field name="scrap_policy" invisible="True" />
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
1
rma_scrap/wizards/__init__.py
Normal file
1
rma_scrap/wizards/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import rma_make_scrap
|
||||||
112
rma_scrap/wizards/rma_make_scrap.py
Normal file
112
rma_scrap/wizards/rma_make_scrap.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Copyright 2022 ForgeFlow S.L.
|
||||||
|
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.html).
|
||||||
|
|
||||||
|
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class RmaMakeScrap(models.TransientModel):
|
||||||
|
_name = "rma_make_scrap.wizard"
|
||||||
|
_description = "Wizard to create scrap from rma lines"
|
||||||
|
|
||||||
|
item_ids = fields.One2many(
|
||||||
|
comodel_name="rma_make_scrap_item.wizard",
|
||||||
|
inverse_name="wiz_id",
|
||||||
|
string="Items",
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.returns("rma.order.line")
|
||||||
|
def _prepare_item(self, line):
|
||||||
|
values = {
|
||||||
|
"product_id": line.product_id.id,
|
||||||
|
"product_qty": line.product_qty,
|
||||||
|
"location_id": line.location_id.id,
|
||||||
|
"uom_id": line.uom_id.id,
|
||||||
|
"qty_to_scrap": line.qty_to_scrap,
|
||||||
|
"line_id": line.id,
|
||||||
|
"rma_id": line.rma_id and line.rma_id.id or False,
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def default_get(self, fields_list):
|
||||||
|
context = self._context.copy()
|
||||||
|
res = super(RmaMakeScrap, self).default_get(fields_list)
|
||||||
|
rma_line_obj = self.env["rma.order.line"]
|
||||||
|
rma_line_ids = self.env.context["active_ids"] or []
|
||||||
|
active_model = self.env.context["active_model"]
|
||||||
|
|
||||||
|
if not rma_line_ids:
|
||||||
|
return res
|
||||||
|
assert active_model == "rma.order.line", "Bad context propagation"
|
||||||
|
|
||||||
|
items = []
|
||||||
|
lines = rma_line_obj.browse(rma_line_ids)
|
||||||
|
for line in lines:
|
||||||
|
items.append([0, 0, self._prepare_item(line)])
|
||||||
|
res["item_ids"] = items
|
||||||
|
context.update({"items_ids": items})
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _create_scrap(self):
|
||||||
|
scraps = []
|
||||||
|
for item in self.item_ids:
|
||||||
|
line = item.line_id
|
||||||
|
if line.state != "approved":
|
||||||
|
raise ValidationError(_("RMA %s is not approved") % line.name)
|
||||||
|
scrap = self._prepare_scrap(item)
|
||||||
|
scraps.append(scrap)
|
||||||
|
return scraps
|
||||||
|
|
||||||
|
def action_create_scrap(self):
|
||||||
|
self._create_scrap()
|
||||||
|
self.item_ids[0].line_id._compute_qty_in_scrap()
|
||||||
|
return self.item_ids[0].line_id.action_view_scrap_transfers()
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _prepare_scrap(self, item):
|
||||||
|
line = item.line_id
|
||||||
|
scrap = self.env["stock.scrap"].create(
|
||||||
|
{
|
||||||
|
"name": line.rma_id.id and line.rma_id.name or line.name,
|
||||||
|
"origin": line.name,
|
||||||
|
"product_id": item.line_id.product_id.id,
|
||||||
|
"scrap_qty": item.qty_to_scrap,
|
||||||
|
"product_uom_id": item.line_id.product_id.product_tmpl_id.uom_id.id,
|
||||||
|
"scrap_location_id": line.operation_id.scrap_location_id.id,
|
||||||
|
"rma_line_id": line.id,
|
||||||
|
"create_date": fields.Datetime.now(),
|
||||||
|
"company_id": line.company_id.id,
|
||||||
|
"is_rma_scrap": True,
|
||||||
|
"location_id": line.location_id.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return scrap
|
||||||
|
|
||||||
|
|
||||||
|
class RmaMakeScrapItem(models.TransientModel):
|
||||||
|
_name = "rma_make_scrap_item.wizard"
|
||||||
|
_description = "Items to Scrap"
|
||||||
|
|
||||||
|
wiz_id = fields.Many2one("rma_make_scrap.wizard", string="Wizard", required=True)
|
||||||
|
line_id = fields.Many2one(
|
||||||
|
"rma.order.line", string="RMA order Line", ondelete="cascade"
|
||||||
|
)
|
||||||
|
rma_id = fields.Many2one("rma.order", related="line_id.rma_id", string="RMA Group")
|
||||||
|
product_id = fields.Many2one("product.product", string="Product")
|
||||||
|
product_qty = fields.Float(
|
||||||
|
related="line_id.product_qty",
|
||||||
|
string="Quantity Ordered",
|
||||||
|
copy=False,
|
||||||
|
digits="Product Unit of Measure",
|
||||||
|
)
|
||||||
|
location_id = fields.Many2one(
|
||||||
|
"stock.location",
|
||||||
|
string="Scrap Location",
|
||||||
|
domain="[('scrap_location', '=', True)]",
|
||||||
|
)
|
||||||
|
qty_to_scrap = fields.Float(
|
||||||
|
string="Quantity To Scrap", digits="Product Unit of Measure"
|
||||||
|
)
|
||||||
|
uom_id = fields.Many2one("uom.uom", string="Unit of Measure")
|
||||||
72
rma_scrap/wizards/rma_scrap_view.xml
Normal file
72
rma_scrap/wizards/rma_scrap_view.xml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_rma_scrap" model="ir.ui.view">
|
||||||
|
<field name="name">Create Scrap</field>
|
||||||
|
<field name="model">rma_make_scrap.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Select lines for picking" name="lines">
|
||||||
|
<separator string="Select lines for Scrap" />
|
||||||
|
<field name="item_ids">
|
||||||
|
<tree string="RMA Lines" editable="bottom" create="false">
|
||||||
|
<field name="rma_id" groups="rma.group_rma_groups" />
|
||||||
|
<field name="product_id" />
|
||||||
|
<field name="product_qty" />
|
||||||
|
<field name="qty_to_scrap" />
|
||||||
|
<field name="location_id" />
|
||||||
|
<field name="line_id" invisible="1" />
|
||||||
|
<field name="uom_id" groups="uom.group_uom" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
<footer>
|
||||||
|
<button
|
||||||
|
string="Confirm"
|
||||||
|
name="action_create_scrap"
|
||||||
|
type="object"
|
||||||
|
class="oe_highlight"
|
||||||
|
/>
|
||||||
|
or
|
||||||
|
<button
|
||||||
|
name="action_cancel"
|
||||||
|
string="Cancel"
|
||||||
|
class="oe_link"
|
||||||
|
special="cancel"
|
||||||
|
/>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_rma_scrap" model="ir.actions.act_window">
|
||||||
|
<field name="name">Create Scrap</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">rma_make_scrap.wizard</field>
|
||||||
|
<field name="view_mode">form</field>
|
||||||
|
<field name="target">new</field>
|
||||||
|
<field name="view_id" ref="view_rma_scrap" />
|
||||||
|
<field name="groups_id" eval="[(4, ref('stock.group_stock_user'))]" />
|
||||||
|
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="view_rma_line_button_scrap_form" model="ir.ui.view">
|
||||||
|
<field name="name">rma.order.line.form</field>
|
||||||
|
<field name="model">rma.order.line</field>
|
||||||
|
<field name="inherit_id" ref="rma.view_rma_line_button_form" />
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<header position="inside">
|
||||||
|
<button
|
||||||
|
name="%(action_rma_scrap)d"
|
||||||
|
string="Scrap"
|
||||||
|
class="oe_highlight"
|
||||||
|
attrs="{'invisible':['|', ('qty_to_scrap', '=', 0), ('state', '!=', 'approved')]}"
|
||||||
|
type="action"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
name="%(action_rma_scrap)d"
|
||||||
|
string="Scrap"
|
||||||
|
attrs="{'invisible':['|', ('qty_to_scrap', '!=', 0), ('state', '!=', 'approved')]}"
|
||||||
|
type="action"
|
||||||
|
/>
|
||||||
|
</header>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Reference in New Issue
Block a user