mirror of
https://github.com/ForgeFlow/stock-rma.git
synced 2025-01-21 12:57:49 +02:00
[14.0][IMP]rma_put_away: allows to do stock moves from rma
This commit is contained in:
1
rma_free_of_charge_true/__init__.py
Normal file
1
rma_free_of_charge_true/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
16
rma_free_of_charge_true/__manifest__.py
Normal file
16
rma_free_of_charge_true/__manifest__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "RMA Free of Charge True",
|
||||
"summary": "Make the field free of charge true by default",
|
||||
"version": "14.0.1.1.0",
|
||||
"author": "ForgeFlow, " "Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/ForgeFlow/stock-rma",
|
||||
"category": "RMA",
|
||||
"depends": ["account_move_line_rma_order_line"],
|
||||
"license": "AGPL-3",
|
||||
"data": [
|
||||
|
||||
],
|
||||
"installable": True,
|
||||
"maintainers": ["DavidJForgeFlow"],
|
||||
"development_status": "Beta",
|
||||
}
|
||||
1
rma_free_of_charge_true/models/__init__.py
Normal file
1
rma_free_of_charge_true/models/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import rma_line_make_purchase_order_item
|
||||
@@ -0,0 +1,7 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class RmaLineMakePurchaseOrderItem(models.TransientModel):
|
||||
_inherit = "rma.order.line.make.purchase.order.item"
|
||||
|
||||
free_of_charge = fields.Boolean(default="True")
|
||||
2
rma_put_away/__init__.py
Normal file
2
rma_put_away/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import wizards
|
||||
from . import models
|
||||
18
rma_put_away/__manifest__.py
Normal file
18
rma_put_away/__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 recieved products"
|
||||
"in odoo",
|
||||
"author": "ForgeFlow",
|
||||
"website": "https://github.com/ForgeFlow/stock-rma",
|
||||
"depends": ["rma"],
|
||||
"data": [
|
||||
"views/rma_put_away_view.xml",
|
||||
"views/rma_operation_view.xml",
|
||||
"views/rma_order_view.xml",
|
||||
"views/rma_order_line_view.xml",
|
||||
],
|
||||
"installable": True,
|
||||
}
|
||||
2
rma_put_away/models/__init__.py
Normal file
2
rma_put_away/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import rma_order_line
|
||||
from . import rma_operation
|
||||
33
rma_put_away/models/rma_operation.py
Normal file
33
rma_put_away/models/rma_operation.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# Copyright 2020 ForgeFlow S.L.
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class RmaOperation(models.Model):
|
||||
_inherit = "rma.operation"
|
||||
|
||||
put_away_policy = fields.Selection(
|
||||
selection=[
|
||||
("no", "Not required"),
|
||||
("ordered", "Based on Ordered Quantities"),
|
||||
("received", "Based on Received Quantities"),
|
||||
],
|
||||
string="Put Away Policy",
|
||||
default="no",
|
||||
)
|
||||
|
||||
internal_route_id = fields.Many2one(
|
||||
comodel_name="stock.location.route",
|
||||
string="Internal Route",
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
default=lambda self: self._default_routes(),
|
||||
)
|
||||
|
||||
internal_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Internal Destination Warehouse",
|
||||
default=lambda self: self._default_warehouse_id(),
|
||||
)
|
||||
|
||||
|
||||
52
rma_put_away/models/rma_order_line.py
Normal file
52
rma_put_away/models/rma_order_line.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class RmaOrderLine(models.Model):
|
||||
_inherit = "rma.order.line"
|
||||
|
||||
qty_to_put_away = fields.Float(
|
||||
string="Qty To Put Away",
|
||||
digits="Product Unit of Measure",
|
||||
compute="_compute_qty_to_put_away",
|
||||
store=True,
|
||||
)
|
||||
|
||||
qty_put_away = fields.Float(
|
||||
string="Qty Put Away",
|
||||
copy=False,
|
||||
digits="Product Unit of Measure",
|
||||
readonly=True,
|
||||
compute="_compute_qty_put_away",
|
||||
store=True,
|
||||
help="Quantity Put Away.",
|
||||
)
|
||||
|
||||
put_away_policy = fields.Selection(
|
||||
selection=[
|
||||
("no", "Not required"),
|
||||
("ordered", "Based on Ordered Quantities"),
|
||||
("received", "Based on Received Quantities"),
|
||||
],
|
||||
string="Put Away Policy",
|
||||
default="no",
|
||||
required=True,
|
||||
)
|
||||
|
||||
def _compute_qty_to_put_away(self):
|
||||
for rec in self:
|
||||
rec.qty_to_put_away = 0.0
|
||||
if rec.put_away_policy == "ordered":
|
||||
rec.qty_to_put_away = rec.product_qty - rec.qty_put_away
|
||||
elif rec.put_away_policy == "received":
|
||||
rec.qty_to_put_away = rec.qty_received - rec.qty_put_away
|
||||
|
||||
def _compute_qty_put_away(self):
|
||||
for rec in self:
|
||||
rec.qty_put_away = 0.0
|
||||
|
||||
@api.onchange("operation_id")
|
||||
def _onchange_operation_id(self):
|
||||
result = super(RmaOrderLine, self)._onchange_operation_id()
|
||||
if self.operation_id:
|
||||
self.put_away_policy = self.operation_id.put_away_policy or "no"
|
||||
return result
|
||||
5
rma_put_away/security/ir.model.access.csv
Normal file
5
rma_put_away/security/ir.model.access.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_rma_put_away_wizard,rma.order.manager,model_rma_make_put_away.wizard,group_rma_manager,1,1,1,1
|
||||
access_rma_put_away_wizard_customer,rma.order.manager,model_rma_make_put_away.wizard,group_rma_customer_user,1,1,1,1
|
||||
access_rma_put_away_wizard_supplier,rma.order.manager,model_rma_make_put_away.wizard,group_rma_supplier_user,1,1,1,1
|
||||
|
||||
|
32
rma_put_away/views/rma_operation_view.xml
Normal file
32
rma_put_away/views/rma_operation_view.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="rma_operation_tree" model="ir.ui.view">
|
||||
<field name="name">rma.operation.tree - rma_put_away</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="put_away_policy" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="rma_operation_form" model="ir.ui.view">
|
||||
<field name="name">rma.operation.form - rma_put_away</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="put_away_policy" />
|
||||
</field>
|
||||
<group name="outbound" position="after">
|
||||
<group name="internal" string="Internal">
|
||||
<field name="internal_route_id"/>
|
||||
<field name="internal_warehouse_id"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
15
rma_put_away/views/rma_order_line_view.xml
Normal file
15
rma_put_away/views/rma_order_line_view.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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 - rma_put_away</field>
|
||||
<field name="model">rma.order.line</field>
|
||||
<field name="inherit_id" ref="rma.view_rma_line_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="delivery_policy" position="after">
|
||||
<field name="put_away_policy" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
15
rma_put_away/views/rma_order_view.xml
Normal file
15
rma_put_away/views/rma_order_view.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_rma_form" model="ir.ui.view">
|
||||
<field name="name">rma.order.form - rma_put_away</field>
|
||||
<field name="model">rma.order</field>
|
||||
<field name="inherit_id" ref="rma.view_rma_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='rma_line_ids']/tree" position="inside">
|
||||
<field name="put_away_policy" invisible="True" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
76
rma_put_away/views/rma_put_away_view.xml
Normal file
76
rma_put_away/views/rma_put_away_view.xml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<record id="view_rma_put_away" model="ir.ui.view">
|
||||
<field name="name">Create Put Away</field>
|
||||
<field name="model">rma_make_put_away.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Select lines for picking" name="lines">
|
||||
<separator string="Select lines for put away" />
|
||||
<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="line_id" invisible="1" />
|
||||
<field name="uom_id" groups="uom.group_uom" />
|
||||
</tree>
|
||||
</field>
|
||||
<footer>
|
||||
<button
|
||||
string="Confirm"
|
||||
name="action_create_put_away"
|
||||
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_put_away" model="ir.actions.act_window">
|
||||
<field name="name">Create Put Away</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">rma_make_put_away.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="view_id" ref="view_rma_put_away" />
|
||||
<field name="groups_id" eval="[(4, ref('stock.group_stock_user'))]" />
|
||||
<field name="context">{'picking_type': 'internal'}</field>
|
||||
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||
</record>
|
||||
|
||||
<record id="view_rma_line_button_put_away_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_put_away)d"
|
||||
string="Create Put Away"
|
||||
class="oe_highlight"
|
||||
attrs="{'invisible':['|', ('qty_to_put_away', '=', 0), ('state', '!=', 'approved')]}"
|
||||
type="action"
|
||||
/>
|
||||
<button
|
||||
name="%(action_rma_put_away)d"
|
||||
string="Create Put Away"
|
||||
attrs="{'invisible':['|', ('qty_to_put_away', '!=', 0), ('state', '!=', 'approved')]}"
|
||||
type="action"
|
||||
/>
|
||||
<field
|
||||
name="qty_to_put_away"
|
||||
widget="statinfo"
|
||||
string="PutAwayQty"
|
||||
/>
|
||||
</header>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
1
rma_put_away/wizards/__init__.py
Normal file
1
rma_put_away/wizards/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import rma_make_put_away
|
||||
177
rma_put_away/wizards/rma_make_put_away.py
Normal file
177
rma_put_away/wizards/rma_make_put_away.py
Normal file
@@ -0,0 +1,177 @@
|
||||
import time
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT
|
||||
|
||||
|
||||
class RmaMakePutAway(models.TransientModel):
|
||||
_name = "rma_make_put_away.wizard"
|
||||
_description = "Wizard to create put away from rma lines"
|
||||
|
||||
item_ids = fields.One2many("rma_make_picking.wizard.item", "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,
|
||||
"uom_id": line.uom_id.id,
|
||||
"qty_to_receive": line.qty_to_receive,
|
||||
"qty_to_deliver": line.qty_to_deliver,
|
||||
"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):
|
||||
"""Default values for wizard, if there is more than one supplier on
|
||||
lines the supplier field is empty otherwise is the unique line
|
||||
supplier.
|
||||
"""
|
||||
context = self._context.copy()
|
||||
res = super(RmaMakePutAway, 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)
|
||||
if len(lines.mapped("partner_id")) > 1:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Only RMA lines from the same partner can be processed at "
|
||||
"the same time"
|
||||
)
|
||||
)
|
||||
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_put_away(self):
|
||||
"""Method called when the user clicks on create picking"""
|
||||
picking_type = "internal"
|
||||
procurements = []
|
||||
for item in self.item_ids:
|
||||
line = item.line_id
|
||||
if line.state != "approved":
|
||||
raise ValidationError(_("RMA %s is not approved") % line.name)
|
||||
if line.receipt_policy == "no" and picking_type == "incoming":
|
||||
raise ValidationError(_("No shipments needed for this operation"))
|
||||
if line.delivery_policy == "no" and picking_type == "outgoing":
|
||||
raise ValidationError(_("No deliveries needed for this operation"))
|
||||
procurement = self._create_procurement(item, picking_type)
|
||||
procurements.extend(procurement)
|
||||
return procurements
|
||||
|
||||
def action_create_put_away(self):
|
||||
self._create_put_away()
|
||||
action = self.item_ids.line_id.action_view_in_shipments()
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def _get_address(self, item):
|
||||
if item.line_id.customer_to_supplier:
|
||||
delivery_address = item.line_id.supplier_address_id
|
||||
elif item.line_id.supplier_to_customer:
|
||||
delivery_address = item.line_id.customer_address_id
|
||||
elif item.line_id.delivery_address_id:
|
||||
delivery_address = item.line_id.delivery_address_id
|
||||
elif item.line_id.partner_id:
|
||||
delivery_address = item.line_id.partner_id
|
||||
else:
|
||||
raise ValidationError(_("Unknown delivery address"))
|
||||
return delivery_address
|
||||
|
||||
@api.model
|
||||
def _get_address_location(self, delivery_address_id, a_type):
|
||||
if a_type == "supplier":
|
||||
return delivery_address_id.property_stock_supplier
|
||||
elif a_type == "customer":
|
||||
return delivery_address_id.property_stock_customer
|
||||
|
||||
@api.model
|
||||
def _get_procurement_data(self, item, group, qty, picking_type):
|
||||
line = item.line_id
|
||||
delivery_address_id = self._get_address(item)
|
||||
location = self._get_address_location(delivery_address_id, line.type)
|
||||
warehouse = line.out_warehouse_id
|
||||
route = line.out_route_id
|
||||
if not route:
|
||||
raise ValidationError(_("No route specified"))
|
||||
if not warehouse:
|
||||
raise ValidationError(_("No warehouse specified"))
|
||||
procurement_data = {
|
||||
"name": line.rma_id and line.rma_id.name or line.name,
|
||||
"group_id": group,
|
||||
"origin": line.name,
|
||||
"warehouse_id": warehouse,
|
||||
"date_planned": time.strftime(DT_FORMAT),
|
||||
"product_id": item.product_id,
|
||||
"product_qty": qty,
|
||||
"partner_id": delivery_address_id.id,
|
||||
"product_uom": line.product_id.product_tmpl_id.uom_id.id,
|
||||
"location_id": location,
|
||||
"rma_line_id": line.id,
|
||||
"route_ids": route,
|
||||
}
|
||||
return procurement_data
|
||||
|
||||
@api.model
|
||||
def _create_procurement(self, item, picking_type):
|
||||
errors = []
|
||||
group = self.find_procurement_group(item)
|
||||
if not group:
|
||||
pg_data = self._get_procurement_group_data(item)
|
||||
group = self.env["procurement.group"].create(pg_data)
|
||||
qty = item.product_qty
|
||||
values = self._get_procurement_data(item, group, qty, picking_type)
|
||||
values = dict(values, rma_line_id=item.line_id, rma_id=item.line_id.rma_id)
|
||||
# create picking
|
||||
procurements = []
|
||||
try:
|
||||
procurement = group.Procurement(
|
||||
item.line_id.product_id,
|
||||
qty,
|
||||
item.line_id.product_id.product_tmpl_id.uom_id,
|
||||
values.get("location_id"),
|
||||
values.get("origin"),
|
||||
values.get("origin"),
|
||||
self.env.company,
|
||||
values,
|
||||
)
|
||||
|
||||
procurements.append(procurement)
|
||||
self.env["procurement.group"].run(procurements)
|
||||
except UserError as error:
|
||||
errors.append(error.name)
|
||||
if errors:
|
||||
raise UserError("\n".join(errors))
|
||||
return procurements
|
||||
|
||||
def find_procurement_group(self, item):
|
||||
if item.line_id.rma_id:
|
||||
return self.env["procurement.group"].search(
|
||||
[("rma_id", "=", item.line_id.rma_id.id)]
|
||||
)
|
||||
else:
|
||||
return self.env["procurement.group"].search(
|
||||
[("rma_line_id", "=", item.line_id.id)]
|
||||
)
|
||||
|
||||
def _get_procurement_group_data(self, item):
|
||||
group_data = {
|
||||
"partner_id": item.line_id.partner_id.id,
|
||||
"name": item.line_id.rma_id.name or item.line_id.name,
|
||||
"rma_id": item.line_id.rma_id and item.line_id.rma_id.id or False,
|
||||
"rma_line_id": item.line_id.id if not item.line_id.rma_id else False,
|
||||
}
|
||||
return group_data
|
||||
|
||||
Reference in New Issue
Block a user