init branch

This commit is contained in:
Jordi Ballester
2017-07-27 18:17:19 +02:00
committed by ahenriquez
parent 571e437f16
commit 8939fc8c7a
39 changed files with 3569 additions and 0 deletions

96
rma/README.rst Normal file
View File

@@ -0,0 +1,96 @@
.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
:alt: License LGPL-3
RMA
===
A return merchandise authorization (RMA), is a part of the process of
returning a product in order to receive a refund, replacement, or repair
during the product's warranty period.
The purchaser of the product must contact the manufacturer (or distributor
or retailer) to obtain authorization to return the product.
The resulting RMA number must be displayed on or included in the returned
product's packaging.
The issuance of an RMA is a key gatekeeping moment in the reverse logistics
cycle, providing the vendor with a final opportunity to diagnose and correct
the customer's problem with the product (such as improper installation or
configuration) before the customer permanently relinquishes ownership
of the product to the manufacturer, commonly referred to as a return.
As returns are costly for the vendor and inconvenient for the customer,
any return that can be prevented benefits both parties.
Configuration
=============
Security
--------
Go to Settings > Users and assign the appropiate permissions to users.
Different security groups grant distinct levels of access to the RMA features.
* Users in group "RMA Customer User" or "RMA Supplier User" can access to,
create and process RMA's associated to customers or suppliers respectively.
* Users in group "RMA Manager" can access to, create, approve and process RMA's
associated to both customers and suppliers.
#. Go to Settings > Users and assign the appropiate permissions to users.
Users assigned to the group 'RMA
#. Go to Inventory > Settings > Return Merchandising Authorization and select
the option "Display 3 fields on rma: partner, invoice address, delivery
address" if needed.
#. Go to Inventory > Settings > Configuration > Warehouse management >
Warehouses and add a default RMA location and RMA picking type for customers
and suppliers RMA picking type. In case the warehouse is configured to
use routes, you need to create at least one route per rma type with at
least two push rules (one for inbound another for outbound) it's very
important to select the typeof operation supplier if we are moving in the
company and customer if we are moving out of the company.
Usage
=====
RMA are accessible though Inventory menu. There's four menus, divided by type
. Users can access to the list of RMA or RMA lines.
Create an RMA:
#. Select a partner. Enter RMA lines associated to an existing picking, or
manually.
#. Request approval and approve.
#. Click on RMA Lines button.
#. Click on more and select an option: "Receive products", "Create Delivery
Order".
#. Go back to the RMA. Set the RMA to done if not further action is required.
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
------------
* Jordi Ballester Alomar <jordi.ballester@eficent.com>
* Aaron Henriquez <ahenriquez@eficent.com>
Maintainer
----------
This module is maintained by Eficent.

5
rma/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from . import models
from . import wizards

41
rma/__openerp__.py Normal file
View File

@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
{
'name': 'rma',
'version': '9.0.1.0.0',
'license': 'LGPL-3',
'category': 'RMA',
'summary': 'Introduces the return merchandise authorization (RMA) process '
'in odoo',
'author': "Eficent",
'website': 'http://www.github.com/OCA/rma',
'depends': ['account', 'stock', 'mail',
'procurement'],
'demo': [
'demo/stock_demo.xml',
],
'data': ['security/rma.xml',
'security/ir.model.access.csv',
'data/rma_sequence.xml',
'data/stock_data.xml',
'data/rma_operation.xml',
'views/rma_order_view.xml',
'views/rma_operation_view.xml',
'views/rma_rule_view.xml',
'views/rma_order_line_view.xml',
'views/stock_view.xml',
'views/stock_warehouse.xml',
'views/product_view.xml',
'views/procurement_view.xml',
'views/res_company_view.xml',
'wizards/rma_make_picking_view.xml',
'wizards/rma_add_stock_move_view.xml',
'wizards/stock_config_settings.xml',
'wizards/rma_order_line_make_supplier_rma_view.xml',
],
'installable': True,
'auto_install': False,
}

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="rma_operation_customer_replace" model="rma.operation">
<field name="name">Replace After Receive</field>
<field name="code">RPLC</field>
<field name="refund_policy">no</field>
<field name="receipt_policy">ordered</field>
<field name="delivery_policy">received</field>
<field name="type">customer</field>
<field name="in_route_id" ref="rma.route_rma_customer"/>
<field name="out_route_id" ref="rma.route_rma_customer"/>
</record>
<record id="rma_operation_supplier_replace" model="rma.operation">
<field name="name">Replace</field>
<field name="code">RPLS</field>
<field name="refund_policy">no</field>
<field name="receipt_policy">ordered</field>
<field name="delivery_policy">ordered</field>
<field name="type">supplier</field>
<field name="in_route_id" ref="rma.route_rma_supplier"/>
<field name="out_route_id" ref="rma.route_rma_supplier"/>
</record>
<record id="rma_operation_customer_refund" model="rma.operation">
<field name="name">Refund before receive</field>
<field name="code">RFC</field>
<field name="refund_policy">ordered</field>
<field name="receipt_policy">no</field>
<field name="delivery_policy">no</field>
<field name="type">customer</field>
<field name="in_route_id" ref="rma.route_rma_customer"/>
</record>
<record id="rma_operation_supplier_refund" model="rma.operation">
<field name="name">Refund only</field>
<field name="code">RFS</field>
<field name="refund_policy">ordered</field>
<field name="receipt_policy">no</field>
<field name="delivery_policy">no</field>
<field name="type">supplier</field>
<field name="out_route_id" ref="rma.route_rma_supplier"/>
</record>
<record id="rma_operation_ds_replace" model="rma.operation">
<field name="name">Replace deliver to vendor</field>
<field name="code">DSRPLB</field>
<field name="refund_policy">no</field>
<field name="receipt_policy">ordered</field>
<field name="delivery_policy">no</field>
<field name="type">customer</field>
<field name="out_route_id" ref="rma.route_rma_dropship"/>
</record>
<record id="rma_operation_ds_replace_customer" model="rma.operation">
<field name="name">Replace receive in vendor</field>
<field name="code">DSRPLV</field>
<field name="refund_policy">no</field>
<field name="receipt_policy">no</field>
<field name="delivery_policy">received</field>
<field name="type">customer</field>
<field name="out_route_id" ref="rma.route_rma_dropship"/>
</record>
<record id="rma_operation_ds_replace_supplier" model="rma.operation">
<field name="name">Replace deliver to customer</field>
<field name="code">DSRPC</field>
<field name="refund_policy">no</field>
<field name="receipt_policy">no</field>
<field name="delivery_policy">received</field>
<field name="type">supplier</field>
<field name="out_route_id" ref="rma.route_rma_dropship"/>
</record>
<record id="rma_rule_always" model="rma.rule">
<field name="name">Always</field>
<field name="code">alw</field>
<field name="approval_policy">always</field>
</record>
</odoo>

39
rma/data/rma_sequence.xml Normal file
View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="rma_seq" model="ir.sequence">
<field name="name">Customer RMA sequence</field>
<field name="code">rma.order.customer</field>
<field name="padding">5</field>
<field name="prefix">RMA/%(year)s/</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
</record>
<record id="rma_supplier_seq" model="ir.sequence">
<field name="name">Supplier RMA sequence</field>
<field name="code">rma.order.supplier</field>
<field name="padding">5</field>
<field name="prefix">RTV/%(year)s/</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
</record>
<record id="rma_line_seq" model="ir.sequence">
<field name="name">Customer RMA Line sequence</field>
<field name="code">rma.order.line.customer</field>
<field name="padding">5</field>
<field name="prefix">RMAL/%(year)s/</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
</record>
<record id="rma_line_supplier_seq" model="ir.sequence">
<field name="name">Supplier RMA Line sequence</field>
<field name="code">rma.order.line.supplier</field>
<field name="padding">5</field>
<field name="prefix">RTVL/%(year)s/</field>
<field eval="1" name="number_next"/>
<field eval="1" name="number_increment"/>
</record>
</odoo>

212
rma/data/stock_data.xml Normal file
View File

@@ -0,0 +1,212 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="location_rma" model="stock.location">
<field name="name">WH RMA</field>
<field name="usage">internal</field>
<field name="location_id" ref="stock.stock_location_stock"/>
</record>
<record id="seq_picking_type_rma_cust_out" model="ir.sequence">
<field name="name">DOC</field>
<field name="prefix">RCO</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="seq_picking_type_rma_cust_in" model="ir.sequence">
<field name="name">INC</field>
<field name="prefix">RCI</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="seq_picking_type_rma_sup_out" model="ir.sequence">
<field name="name">DOS</field>
<field name="prefix">RSO</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="seq_picking_type_rma_sup_in" model="ir.sequence">
<field name="name">INS</field>
<field name="prefix">RSI</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="seq_picking_type_rma_dropship" model="ir.sequence">
<field name="name">DSS</field>
<field name="prefix">DSS</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="picking_type_rma_cust_out" model="stock.picking.type">
<field name="name">RMA → Customer</field>
<field name="sequence_id" ref="seq_picking_type_rma_cust_out"/>
<field name="default_location_src_id" ref="rma.location_rma"/>
<field name="default_location_dest_id"
ref="stock.stock_location_customers"/>
<field name="warehouse_id" eval="False"/>
<field name="code">outgoing</field>
</record>
<record id="picking_type_rma_cust_in" model="stock.picking.type">
<field name="name">Customer → RMA</field>
<field name="sequence_id" ref="seq_picking_type_rma_cust_in"/>
<field name="default_location_src_id" ref="stock.stock_location_customers"/>
<field name="default_location_dest_id"
ref="rma.location_rma"/>
<field name="warehouse_id" eval="False"/>
<field name="code">incoming</field>
</record>
<record id="picking_type_rma_sup_out" model="stock.picking.type">
<field name="name">RMA -> Supplier</field>
<field name="sequence_id" ref="seq_picking_type_rma_sup_out"/>
<field name="default_location_src_id" ref="rma.location_rma"/>
<field name="default_location_dest_id"
ref="stock.stock_location_suppliers"/>
<field name="warehouse_id" eval="False"/>
<field name="code">outgoing</field>
</record>
<record id="picking_type_rma_sup_in" model="stock.picking.type">
<field name="name">Supplier -> RMA</field>
<field name="sequence_id" ref="seq_picking_type_rma_sup_in"/>
<field name="default_location_src_id" ref="stock.stock_location_suppliers"/>
<field name="default_location_dest_id"
ref="rma.location_rma"/>
<field name="warehouse_id" eval="False"/>
<field name="code">incoming</field>
</record>
<record id="picking_type_rma_dropship_return" model="stock.picking.type">
<field name="name">Customer -> Supplier</field>
<field name="sequence_id" ref="seq_picking_type_rma_dropship"/>
<field name="default_location_src_id"
ref="stock.stock_location_customers"/>
<field name="default_location_dest_id"
ref="stock.stock_location_suppliers"/>
<field name="warehouse_id" eval="False"/>
<field name="code">incoming</field>
</record>
<record id="picking_type_rma_dropship" model="stock.picking.type">
<field name="name">Supplier -> Customer</field>
<field name="sequence_id" ref="seq_picking_type_rma_dropship"/>
<field name="default_location_src_id"
ref="stock.stock_location_suppliers"/>
<field name="default_location_dest_id"
ref="stock.stock_location_customers"/>
<field name="warehouse_id" eval="False"/>
<field name="code">incoming</field>
</record>
<record id="stock.warehouse0" model="stock.warehouse">
<field name="lot_rma_id" ref="location_rma"/>
<field name="rma_cust_out_type_id" ref="picking_type_rma_cust_out"/>
<field name="rma_cust_in_type_id" ref="picking_type_rma_cust_in"/>
<field name="rma_sup_out_type_id" ref="picking_type_rma_sup_out"/>
<field name="rma_sup_in_type_id" ref="picking_type_rma_sup_in"/>
</record>
<record id="route_rma_customer" model='stock.location.route'>
<field name="name">RMA Customer</field>
<field name="sequence">10</field>
<field name="sale_selectable" eval="False"/>
<field name="product_selectable" eval="False"/>
<field name="product_categ_selectable" eval="False"/>
<field name="warehouse_selectable" eval="False"/>
<field name="rma_selectable" eval="True"/>
</record>
<record id="route_rma_supplier" model='stock.location.route'>
<field name="name">RMA Supplier</field>
<field name="sequence">10</field>
<field name="sale_selectable" eval="False"/>
<field name="product_selectable" eval="False"/>
<field name="product_categ_selectable" eval="False"/>
<field name="warehouse_selectable" eval="False"/>
<field name="rma_selectable" eval="True"/>
</record>
<record id="route_rma_dropship" model='stock.location.route'>
<field name="name">RMA Dropship</field>
<field name="sequence">10</field>
<field name="sale_selectable" eval="False"/>
<field name="product_selectable" eval="False"/>
<field name="product_categ_selectable" eval="False"/>
<field name="warehouse_selectable" eval="False"/>
<field name="rma_selectable" eval="True"/>
</record>
<record id="rule_rma_customer_in_pull" model="procurement.rule">
<field name="name">Customer → RMA</field>
<field name="action">move</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_src_id" ref="stock.stock_location_customers"/>
<field name="location_id" ref="rma.location_rma"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="rma.route_rma_customer"/>
<field name="picking_type_id" ref="rma.picking_type_rma_cust_in"/>
</record>
<record id="rule_rma_customer_out_pull" model="procurement.rule">
<field name="name">RMA → Customer</field>
<field name="action">move</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_src_id" ref="rma.location_rma"/>
<field name="location_id" ref="stock.stock_location_customers"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="rma.route_rma_customer"/>
<field name="picking_type_id" ref="rma.picking_type_rma_cust_out"/>
</record>
<record id="rule_rma_supplier_out_pull" model="procurement.rule">
<field name="name">RMA → Supplier</field>
<field name="action">move</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_id" ref="stock.stock_location_suppliers"/>
<field name="location_src_id" ref="rma.location_rma"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="rma.route_rma_supplier"/>
<field name="picking_type_id" ref="rma.picking_type_rma_sup_out"/>
</record>
<record id="rule_rma_supplier_in_pull" model="procurement.rule">
<field name="name">Supplier → RMA</field>
<field name="action">move</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_id" ref="rma.location_rma"/>
<field name="location_src_id" ref="stock.stock_location_suppliers"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="rma.route_rma_supplier"/>
<field name="picking_type_id" ref="rma.picking_type_rma_sup_in"/>
</record>
<record id="rule_rma_dropship_return" model="procurement.rule">
<field name="name">Customer → Supplier</field>
<field name="action">move</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_id" ref="stock.stock_location_suppliers"/>
<field name="location_src_id" ref="stock.stock_location_customers"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="rma.route_rma_dropship"/>
<field name="picking_type_id"
ref="rma.picking_type_rma_dropship_return"/>
</record>
<record id="rule_rma_dropship" model="procurement.rule">
<field name="name">Supplier → Customer</field>
<field name="action">move</field>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_id" ref="stock.stock_location_customers"/>
<field name="location_src_id" ref="stock.stock_location_suppliers"/>
<field name="procure_method">make_to_stock</field>
<field name="route_id" ref="rma.route_rma_dropship"/>
<field name="picking_type_id"
ref="rma.picking_type_rma_dropship"/>
</record>
</odoo>

19
rma/demo/stock_demo.xml Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="location_rma_shop0" model="stock.location">
<field name="name">RMA</field>
<field name="usage">internal</field>
<field name="location_id" ref="stock.stock_location_shop0"/>
</record>
<record id="stock.stock_warehouse_shop0" model="stock.warehouse">
<field name="lot_rma_id" ref="location_rma_shop0"/>
<field name="rma_cust_out_type_id" ref="picking_type_rma_cust_out"/>
<field name="rma_cust_in_type_id" ref="picking_type_rma_cust_in"/>
<field name="rma_sup_out_type_id" ref="picking_type_rma_sup_out"/>
<field name="rma_sup_in_type_id" ref="picking_type_rma_sup_in"/>
</record>
</odoo>

13
rma/models/__init__.py Normal file
View File

@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from . import rma_order
from . import rma_order_line
from . import rma_operation
from . import rma_rule
from . import stock
from . import stock_warehouse
from . import product
from . import procurement
from . import res_company

29
rma/models/procurement.py Normal file
View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import api, fields, models
class ProcurementOrder(models.Model):
_inherit = 'procurement.order'
rma_line_id = fields.Many2one('rma.order.line', 'RMA', ondelete="set null")
@api.model
def _run_move_create(self, procurement):
res = super(ProcurementOrder, self)._run_move_create(procurement)
if procurement.rma_line_id:
line = procurement.rma_line_id
res['rma_line_id'] = line.id
if line.delivery_address_id and line.delivery_address_id.id:
res['partner_id'] = line.delivery_address_id.id
elif line.invoice_line_id.invoice_id.partner_id:
res['partner_id'] = line.invoice_id.partner_id.id
return res
class ProcurementGroup(models.Model):
_inherit = 'procurement.group'
rma_id = fields.Many2one('rma.order', 'RMA', ondelete="set null")

19
rma/models/product.py Normal file
View File

@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import fields, models
class ProductCategory(models.Model):
_inherit = 'product.category'
rma_operation_id = fields.Many2one(
comodel_name="rma.operation", string="RMA Operation")
class ProductTemplate(models.Model):
_inherit = 'product.template'
rma_operation_id = fields.Many2one(
comodel_name="rma.operation", string="RMA Operation")

11
rma/models/res_company.py Normal file
View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import models, fields
class ResCompany(models.Model):
_inherit = 'res.company'
rma_rule_id = fields.Many2one('rma.rule', 'Default RMA Approval Policy')

View File

@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import _, api, fields, models
class RmaOperation(models.Model):
_name = 'rma.operation'
_description = 'RMA Operation'
@api.model
def _default_warehouse_id(self):
company = self.env.user.company_id.id
warehouse = self.env['stock.warehouse'].search(
[('company_id', '=', company)], limit=1)
return warehouse
@api.model
def _default_customer_location_id(self):
return self.env.ref('stock.stock_location_customers') or False
@api.model
def _default_supplier_location_id(self):
return self.env.ref('stock.stock_location_suppliers') or False
name = fields.Char('Description', required=True)
code = fields.Char('Code', required=True)
refund_policy = fields.Selection([
('no', 'No refund'), ('ordered', 'Based on Ordered Quantities'),
('received', 'Based on Received Quantities')], string="Refund Policy",
default='no')
receipt_policy = fields.Selection([
('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'),
('received', 'Based on Delivered Quantities')],
string="Receipts Policy", default='no')
delivery_policy = fields.Selection([
('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'),
('received', 'Based on Received Quantities')],
string="Delivery Policy", default='no')
in_route_id = fields.Many2one(
'stock.location.route', string='Inbound Route',
domain=[('rma_selectable', '=', True)])
out_route_id = fields.Many2one(
'stock.location.route', string='Outbound Route',
domain=[('rma_selectable', '=', True)])
customer_to_supplier= fields.Boolean(
'The customer will send to the supplier', default=False)
supplier_to_customer = fields.Boolean(
'The supplier will send to the customer', default=False)
in_warehouse_id = fields.Many2one('stock.warehouse',
string='Inbound Warehouse',
default=_default_warehouse_id)
out_warehouse_id = fields.Many2one('stock.warehouse',
string='Outbound Warehouse',
default=_default_warehouse_id)
location_id = fields.Many2one(
'stock.location', 'Send To This Company Location')
type = fields.Selection([
('customer', 'Customer'), ('supplier', 'Supplier')],
string="Used in RMA of this type", required=True, default='customer')
rma_line_ids = fields.One2many('rma.order.line', 'operation_id',
'RMA lines')

234
rma/models/rma_order.py Normal file
View File

@@ -0,0 +1,234 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import _, api, fields, models
from openerp.addons import decimal_precision as dp
from openerp.exceptions import UserError
from dateutil.relativedelta import relativedelta
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from datetime import datetime
class RmaOrder(models.Model):
_name = "rma.order"
_inherit = ['mail.thread']
@api.model
def _compute_rule_id(self):
if self.company_id and self.company_id.id:
if self.company_id.rma_rule_id and self.company_id.rma_rule_id.id:
self.rule_id = self.company_id.rma_rule_id
@api.model
def _get_default_type(self):
if 'supplier' in self.env.context:
return "supplier"
else:
return "customer"
@api.multi
def _compute_in_shipment_count(self):
for rec in self:
rec.in_shipment_count = len(rec.rma_line_ids.mapped(
'procurement_ids.move_ids').filtered(
lambda m: m.location_dest_id.usage == 'internal').mapped(
'picking_id'))
@api.multi
def _compute_out_shipment_count(self):
for rec in self:
rec.out_shipment_count = len(rec.rma_line_ids.mapped(
'procurement_ids.move_ids').filtered(
lambda m: m.location_id.usage == 'internal').mapped(
'picking_id'))
@api.multi
def _compute_supplier_line_count(self):
self.supplier_line_count = len(self.rma_line_ids.filtered(
lambda r: r.supplier_rma_line_ids))
@api.multi
def _compute_line_count(self):
self.ensure_one()
self.line_count = len(self._get_valid_lines())
name = fields.Char(string='Order Number', index=True,
readonly=True,
states={'progress': [('readonly', False)]},
copy=False)
type = fields.Selection(
[('customer', 'Customer'), ('supplier', 'Supplier')],
string="Type", required=True, default=_get_default_type, readonly=True)
reference = fields.Char(string='Reference',
help="The partner reference of this RMA order.")
comment = fields.Text('Additional Information', readonly=True, states={
'draft': [('readonly', False)]})
state = fields.Selection([('draft', 'Draft'), ('to_approve', 'To Approve'),
('approved', 'Approved'),
('done', 'Done')], string='State', index=True,
default='draft')
date_rma = fields.Datetime(string='Order Date', index=True, copy=False)
partner_id = fields.Many2one('res.partner', string='Partner',
required=True, readonly=True,
states={'draft': [('readonly', False)]})
assigned_to = fields.Many2one('res.users', 'Assigned to',
track_visibility='onchange')
requested_by = fields.Many2one('res.users', 'Requested by',
track_visibility='onchange',
default=lambda self: self.env.user)
rule_id = fields.Many2one('rma.rule', string='Approval Criteria',
compute=_compute_rule_id)
rma_line_ids = fields.One2many('rma.order.line', 'rma_id',
string='RMA lines')
in_shipment_count = fields.Integer(compute=_compute_in_shipment_count,
string='# of Invoices')
out_shipment_count = fields.Integer(compute=_compute_out_shipment_count,
string='# of Outgoing Shipments')
line_count = fields.Integer(compute=_compute_line_count,
string='# of Outgoing Shipments',
copy=False)
supplier_line_count = fields.Integer(compute=_compute_supplier_line_count,
string='# of Outgoing Shipments',
copy=False)
company_id = fields.Many2one('res.company', string='Company',
required=True, default=lambda self:
self.env.user.company_id)
@api.model
def create(self, vals):
if self.env.context.get('supplier'):
vals['name'] = self.env['ir.sequence'].next_by_code(
'rma.order.supplier')
else:
vals['name'] = self.env['ir.sequence'].next_by_code(
'rma.order.customer')
return super(RmaOrder, self).create(vals)
@api.multi
def action_view_in_shipments(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
picking_ids = []
suppliers = self.env.ref('stock.stock_location_suppliers')
customers = self.env.ref('stock.stock_location_customers')
for line in self.rma_line_ids:
if line.type == 'customer':
for move in line.move_ids:
if move.picking_id.location_id == customers:
picking_ids.append(move.picking_id.id)
else:
for move in line.move_ids:
if move.picking_id.location_id == suppliers:
picking_ids.append(move.picking_id.id)
shipments = list(set(picking_ids))
# choose the view_mode accordingly
if len(shipments) != 1:
result['domain'] = "[('id', 'in', " + \
str(shipments) + ")]"
elif len(shipments) == 1:
res = self.env.ref('stock.view_picking_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = shipments[0]
return result
@api.multi
def action_view_out_shipments(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
picking_ids = []
suppliers = self.env.ref('stock.stock_location_suppliers')
customers = self.env.ref('stock.stock_location_customers')
for line in self.rma_line_ids:
if line.type == 'customer':
for move in line.move_ids:
if move.picking_id.location_id != customers:
picking_ids.append(move.picking_id.id)
else:
for move in line.move_ids:
if move.picking_id.location_id != suppliers:
picking_ids.append(move.picking_id.id)
shipments = list(set(picking_ids))
# choose the view_mode accordingly
if len(shipments) != 1:
result['domain'] = "[('id', 'in', " + \
str(shipments) + ")]"
elif len(shipments) == 1:
res = self.env.ref('stock.view_picking_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = shipments[0]
return result
@api.multi
def action_rma_to_approve(self):
for rec in self:
rec.state = 'to_approve'
if rec.rule_id and rec.rule_id.id:
if rec.rule_id.approval_policy == 'always':
rec.assigned_to = self.env.uid
rec.action_rma_approve()
return True
@api.multi
def action_rma_draft(self):
for rec in self:
rec.state = 'draft'
return True
@api.multi
def action_rma_approve(self):
# pass the supplier address in case this is a customer RMA
for rec in self:
rec.state = 'approved'
return True
@api.multi
def action_rma_done(self):
for rec in self:
rec.state = 'done'
return True
@api.multi
def _get_valid_lines(self):
self.ensure_one()
return self.rma_line_ids
@api.multi
def action_view_lines(self):
if self.type == 'customer':
action = self.env.ref('rma.action_rma_customer_lines')
else:
action = self.env.ref('rma.action_rma_supplier_lines')
result = action.read()[0]
lines = self._get_valid_lines()
# choose the view_mode accordingly
if len(lines) != 1:
result['domain'] = "[('id', 'in', " + \
str(lines.ids) + ")]"
elif len(lines) == 1:
if self.type == 'customer':
res = self.env.ref('rma.view_rma_line_form', False)
else:
res = self.env.ref('rma.view_rma_line_supplier_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = lines.id
return result
@api.multi
def action_view_supplier_lines(self):
action = self.env.ref('rma.action_rma_supplier_lines')
result = action.read()[0]
lines = self.rma_line_ids
related_lines = [line.id for line in lines.children_ids]
# choose the view_mode accordingly
if len(related_lines) != 1:
result['domain'] = "[('id', 'in', " + \
str(related_lines) + ")]"
elif len(related_lines) == 1:
res = self.env.ref('rma.view_rma_line_supplier_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = related_lines[0]
return result

View File

@@ -0,0 +1,423 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import _, api, fields, models
from openerp.addons import decimal_precision as dp
import operator
ops = {'=': operator.eq,
'!=': operator.ne}
class RmaOrderLine(models.Model):
_name = "rma.order.line"
_rec_name = "rma_id"
@api.model
def _default_warehouse_id(self):
rma_id = self.env.context.get('default_rma_id', False)
warehouse = self.env['stock.warehouse']
if rma_id:
rma = self.env['rma.order'].browse(rma_id)
warehouse = self.env['stock.warehouse'].search(
[('company_id', '=', rma.company_id.id)], limit=1)
return warehouse
@api.model
def _default_location_id(self):
wh = self._default_warehouse_id()
return wh.lot_rma_id
@api.model
def _default_delivery_address(self):
partner_id = self.env.context.get('partner_id', False)
if partner_id:
partner = self.env['res.partner'].browse(partner_id)
addr = partner.address_get(['delivery'])
return self.env['res.partner'].browse(addr['delivery'])
return False
@api.multi
def _compute_in_shipment_count(self):
for line in self:
moves = line.procurement_ids.mapped('move_ids').filtered(
lambda m: m.location_dest_id.usage == 'internal')
pickings = moves.mapped('picking_id')
line.in_shipment_count = len(pickings)
@api.multi
def _compute_out_shipment_count(self):
for line in self:
moves = line.procurement_ids.mapped('move_ids').filtered(
lambda m: m.location_dest_id.usage != 'internal')
pickings = moves.mapped('picking_id')
line.out_shipment_count = len(pickings)
@api.multi
def _get_rma_move_qty(self, states, direction='in'):
for rec in self:
product_obj = self.env['product.uom']
qty = 0.0
if direction == 'in':
op = ops['=']
else:
op = ops['!=']
for move in rec.procurement_ids.mapped('move_ids').filtered(
lambda m: m.state in states and op(m.location_id.usage,
rec.type)):
qty += product_obj._compute_qty_obj(
move.product_uom, move.product_uom_qty,
rec.uom_id)
return qty
@api.multi
@api.depends('move_ids', 'move_ids.state', 'qty_received',
'receipt_policy', 'product_qty', 'type')
def _compute_qty_to_receive(self):
for rec in self:
rec.qty_to_receive = 0.0
if rec.receipt_policy == 'ordered':
rec.qty_to_receive = rec.product_qty - rec.qty_received
elif self.receipt_policy == 'delivered':
self.qty_to_receive = rec.qty_delivered - rec.qty_received
@api.multi
@api.depends('move_ids', 'move_ids.state',
'delivery_policy', 'product_qty', 'type', 'qty_delivered',
'qty_received')
def _compute_qty_to_deliver(self):
for rec in self:
rec.qty_to_deliver = 0.0
if rec.delivery_policy == 'ordered':
rec.qty_to_deliver = rec.product_qty - rec.qty_delivered
elif self.delivery_policy == 'received':
self.qty_to_deliver = rec.qty_received - rec.qty_delivered
@api.multi
@api.depends('move_ids', 'move_ids.state', 'type')
def _compute_qty_incoming(self):
for rec in self:
qty = rec._get_rma_move_qty(
('draft', 'confirmed', 'assigned'), direction='in')
rec.qty_incoming = qty
@api.multi
@api.depends('move_ids', 'move_ids.state', 'type')
def _compute_qty_received(self):
for rec in self:
qty = rec._get_rma_move_qty('done', direction='in')
rec.qty_received = qty
@api.multi
@api.depends('move_ids', 'move_ids.state', 'type')
def _compute_qty_outgoing(self):
for rec in self:
qty = rec._get_rma_move_qty(
('draft', 'confirmed', 'assigned'), direction='out')
rec.qty_outgoing = qty
@api.multi
@api.depends('move_ids', 'move_ids.state', 'type')
def _compute_qty_delivered(self):
for rec in self:
qty = rec._get_rma_move_qty('done', direction='out')
rec.qty_delivered = qty
@api.model
def _get_supplier_rma_qty(self):
return sum(self.supplier_rma_line_ids.filtered(
lambda r: r.state != 'cancel').mapped(
'product_qty'))
@api.multi
@api.depends('customer_to_supplier', 'supplier_rma_line_ids',
'supplier_rma_line_ids.rma_id.state',
'move_ids', 'move_ids.state', 'qty_received',
'receipt_policy', 'product_qty', 'type')
def _compute_qty_supplier_rma(self):
for rec in self:
qty = rec._get_supplier_rma_qty()
rec.qty_to_supplier_rma = rec.qty_to_receive - qty
rec.qty_in_supplier_rma = qty
@api.multi
def _compute_procurement_count(self):
for rec in self:
rec.procurement_count = len(rec.procurement_ids.filtered(
lambda p: p.state == 'exception'))
delivery_address_id = fields.Many2one(
'res.partner', string='Partner delivery address',
default=_default_delivery_address,
help="This address will be used to "
"deliver repaired or replacement products.")
rma_id = fields.Many2one('rma.order', string='RMA',
ondelete='cascade', required=True)
name = fields.Char(string='Reference', required=True, default='/',
help='Add here the supplier RMA #. Otherwise an '
'internal code is assigned.')
description = fields.Text(string='Description')
origin = fields.Char(string='Source Document',
help="Reference of the document that produced "
"this rma.")
state = fields.Selection(related='rma_id.state')
operation_id = fields.Many2one(
comodel_name="rma.operation", string="Operation")
assigned_to = fields.Many2one('res.users', related='rma_id.assigned_to')
requested_by = fields.Many2one('res.users', related='rma_id.requested_by')
partner_id = fields.Many2one('res.partner', related='rma_id.partner_id',
store=True)
sequence = fields.Integer(default=10,
help="Gives the sequence of this line "
"when displaying the rma.")
product_id = fields.Many2one('product.product', string='Product',
ondelete='restrict', required=-True)
product_tracking = fields.Selection(related="product_id.tracking")
lot_id = fields.Many2one(
comodel_name="stock.production.lot", string="Lot/Serial Number",
readonly=True, states={"new": [("readonly", False)]},
)
product_qty = fields.Float(
string='Ordered Qty', copy=False,
digits=dp.get_precision('Product Unit of Measure'))
uom_id = fields.Many2one('product.uom', string='Unit of Measure',
required=True)
price_unit = fields.Monetary(string='Price Unit', readonly=False,
states={'approved': [('readonly', True)],
'done': [('readonly', True)],
'to_approve': [('readonly', True)]})
procurement_count = fields.Integer(compute=_compute_procurement_count,
string='# of Procurements', copy=False,
default=0)
in_shipment_count = fields.Integer(compute=_compute_in_shipment_count,
string='# of Shipments', default=0)
out_shipment_count = fields.Integer(compute=_compute_out_shipment_count,
string='# of Deliveries', default=0)
move_ids = fields.One2many('stock.move', 'rma_line_id',
string='Stock Moves', readonly=True
, copy=False)
reference_move_id = fields.Many2one(comodel_name='stock.move',
string='Originating stock move',
readonly=True, copy=False)
procurement_ids = fields.One2many('procurement.order', 'rma_line_id',
string='Procurements', readonly=True,
states={'draft': [('readonly', False)]},
copy=False)
currency_id = fields.Many2one('res.currency', string="Currency")
company_id = fields.Many2one('res.company', string='Company',
related='rma_id.company_id', store=True)
type = fields.Selection(related='rma_id.type')
customer_to_supplier = fields.Boolean(
'The customer will send to the supplier')
supplier_to_customer = fields.Boolean(
'The supplier will send to the customer')
receipt_policy = fields.Selection([
('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'),
('delivered', 'Based on Delivered Quantities')],
required=True, string="Receipts Policy")
delivery_policy = fields.Selection([
('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'),
('received', 'Based on Received Quantities')], required=True,
string="Delivery Policy")
in_route_id = fields.Many2one(
'stock.location.route', string='Inbound Route',
required=True,
domain=[('rma_selectable', '=', True)])
out_route_id = fields.Many2one(
'stock.location.route', string='Outbound Route',
required=True,
domain=[('rma_selectable', '=', True)])
in_warehouse_id = fields.Many2one('stock.warehouse',
string='Inbound Warehouse',
required=True,
default=_default_warehouse_id)
out_warehouse_id = fields.Many2one('stock.warehouse',
string='Outbound Warehouse',
required=True,
default=_default_warehouse_id)
location_id = fields.Many2one(
'stock.location', 'Send To This Company Location', required=True,
default=_default_location_id)
customer_rma_id = fields.Many2one(
'rma.order.line', string='Customer RMA line', ondelete='cascade')
supplier_rma_line_ids = fields.One2many('rma.order.line', 'customer_rma_id')
supplier_address_id = fields.Many2one(
'res.partner', readonly=True,
states={'draft': [('readonly', False)]},
string='Supplier Address',
help="This address of the supplier in case of Customer RMA operation "
"dropship.")
qty_to_receive = fields.Float(
string='Qty To Receive',
digits=dp.get_precision('Product Unit of Measure'),
compute=_compute_qty_to_receive, store=True)
qty_incoming = fields.Float(
string='Incoming Qty', copy=False,
readonly=True, digits=dp.get_precision('Product Unit of Measure'),
compute=_compute_qty_incoming, store=True)
qty_received = fields.Float(
string='Qty Received', copy=False,
digits=dp.get_precision('Product Unit of Measure'),
compute=_compute_qty_received,
store=True)
qty_to_deliver = fields.Float(
string='Qty To Deliver', copy=False,
digits=dp.get_precision('Product Unit of Measure'),
readonly=True, compute=_compute_qty_to_deliver,
store=True)
qty_outgoing = fields.Float(
string='Outgoing Qty', copy=False,
readonly=True, digits=dp.get_precision('Product Unit of Measure'),
compute=_compute_qty_outgoing,
store=True)
qty_delivered = fields.Float(
string='Qty Delivered', copy=False,
digits=dp.get_precision('Product Unit of Measure'),
readonly=True, compute=_compute_qty_delivered,
store=True)
qty_to_supplier_rma = fields.Float(
string='Qty to send to Supplier RMA',
digits=dp.get_precision('Product Unit of Measure'),
readonly=True, compute=_compute_qty_supplier_rma,
store=True)
qty_in_supplier_rma = fields.Float(
string='Qty in Supplier RMA',
digits=dp.get_precision('Product Unit of Measure'),
readonly=True, compute=_compute_qty_supplier_rma,
store=True)
@api.model
def create(self, vals):
if self.env.context.get('supplier'):
vals['name'] = self.env['ir.sequence'].next_by_code(
'rma.order.line.supplier')
else:
vals['name'] = self.env['ir.sequence'].next_by_code(
'rma.order.line.customer')
return super(RmaOrderLine, self).create(vals)
@api.onchange('product_id')
def _onchange_product_id(self):
result = {}
if not self.product_id:
return result
self.product_qty = 1
self.uom_id = self.product_id.uom_id.id
self.price_unit = self.product_id.standard_price
self.operation_id = self.product_id.rma_operation_id or \
self.product_id.categ_id.rma_operation_id
return result
@api.onchange('operation_id')
def _onchange_operation_id(self):
result = {}
if not self.operation_id:
return result
self.receipt_policy = self.operation_id.receipt_policy
self.delivery_policy = self.operation_id.delivery_policy
self.in_warehouse_id = self.operation_id.in_warehouse_id
self.out_warehouse_id = self.operation_id.out_warehouse_id
self.location_id = self.operation_id.location_id or \
self.in_warehouse_id.lot_rma_id
self.customer_to_supplier = self.operation_id.customer_to_supplier
self.supplier_to_customer = self.operation_id.supplier_to_customer
self.in_route_id = self.operation_id.in_route_id
self.out_route_id = self.operation_id.out_route_id
return result
@api.onchange('customer_to_supplier', 'type')
def _onchange_receipt_policy(self):
if self.type == 'supplier' and self.customer_to_supplier:
self.receipt_policy = 'no'
elif self.type == 'customer' and self.supplier_to_customer:
self.delivery_policy = 'no'
@api.onchange('product_id')
def _onchange_product_id(self):
self.uom_id = self.product_id.uom_id
if self.lot_id.product_id != self.product_id:
self.lot_id = False
if self.product_id:
return {'domain': {
'lot_id': [('product_id', '=', self.product_id.id)]}}
return {'domain': {'lot_id': []}}
@api.onchange("lot_id")
def _onchange_lot_id(self):
product = self.lot_id.product_id
if product:
self.product_id = product
self.uom_id = product.uom_id
@api.multi
def action_view_in_shipments(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
picking_ids = []
suppliers = self.env.ref('stock.stock_location_suppliers')
customers = self.env.ref('stock.stock_location_customers')
for line in self:
if line.type == 'customer':
for move in line.move_ids:
if move.picking_id.location_id == customers:
picking_ids.append(move.picking_id.id)
else:
for move in line.move_ids:
if move.picking_id.location_id == suppliers:
picking_ids.append(move.picking_id.id)
shipments = list(set(picking_ids))
# choose the view_mode accordingly
if len(shipments) != 1:
result['domain'] = "[('id', 'in', " + \
str(shipments) + ")]"
elif len(shipments) == 1:
res = self.env.ref('stock.view_picking_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = shipments[0]
return result
@api.multi
def action_view_out_shipments(self):
action = self.env.ref('stock.action_picking_tree_all')
result = action.read()[0]
picking_ids = []
suppliers = self.env.ref('stock.stock_location_suppliers')
customers = self.env.ref('stock.stock_location_customers')
for line in self:
if line.type == 'customer':
for move in line.move_ids:
if move.picking_id.location_id != customers:
picking_ids.append(move.picking_id.id)
else:
for move in line.move_ids:
if move.picking_id.location_id != suppliers:
picking_ids.append(move.picking_id.id)
shipments = list(set(picking_ids))
# choose the view_mode accordingly
if len(shipments) != 1:
result['domain'] = "[('id', 'in', " + \
str(shipments) + ")]"
elif len(shipments) == 1:
res = self.env.ref('stock.view_picking_form', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = shipments[0]
return result
@api.multi
def action_view_procurements(self):
action = self.env.ref('procurement.procurement_order_action_exceptions')
result = action.read()[0]
procurements = self.procurement_ids.filtered(
lambda p: p.state == 'exception').ids
# choose the view_mode accordingly
if len(procurements) != 1:
result['domain'] = "[('id', 'in', " + \
str(procurements) + ")]"
elif len(procurements) == 1:
res = self.env.ref('procurement.procurement_form_view', False)
result['views'] = [(res and res.id or False, 'form')]
result['res_id'] = procurements[0]
return result

16
rma/models/rma_rule.py Normal file
View File

@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import _, api, fields, models
class RmaRule(models.Model):
_name = 'rma.rule'
_description = 'RMA Approval Conditions'
name = fields.Char('Description', required=True)
code = fields.Char('Code', required=True)
approval_policy = fields.Selection([
('always', 'Always')], string="Approval Policy",
required=True, default='always')

37
rma/models/stock.py Normal file
View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import api, fields, models
class StockPicking(models.Model):
_inherit = "stock.picking"
@api.multi
def action_assign(self):
for picking in self:
for move in picking.move_lines:
if len(move.rma_line_id):
if move.state in ('confirmed', 'waiting', 'assigned') \
and move.location_id.usage in (
'supplier', 'customer'):
move.force_assign()
return super(StockPicking, self).action_assign()
class StockMove(models.Model):
_inherit = "stock.move"
rma_line_id = fields.Many2one('rma.order.line', string='RMA',
ondelete='restrict')
@api.model
def create(self, vals):
if vals.get('procurement_id', False):
procurement = self.env['procurement.order'].browse(
vals['procurement_id'])
if procurement.rma_line_id and procurement.rma_line_id.id:
vals['rma_line_id'] = procurement.rma_line_id.id
return super(StockMove, self).create(vals)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import api, fields, models
class StockWarehouse(models.Model):
_inherit = "stock.warehouse"
lot_rma_id = fields.Many2one('stock.location', 'RMA Location')
rma_cust_out_type_id = fields.Many2one('stock.picking.type',
'RMA Customer out Type')
rma_sup_out_type_id = fields.Many2one('stock.picking.type',
'RMA Supplier out Type')
rma_cust_in_type_id = fields.Many2one('stock.picking.type',
'RMA Customer in Type')
rma_sup_in_type_id = fields.Many2one('stock.picking.type',
'RMA Supplier in Type')
class StockLocationRoute(models.Model):
_inherit = "stock.location.route"
rma_selectable = fields.Boolean(string="Selectable on RMA Lines")

View File

@@ -0,0 +1,11 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_rma_user_customer,rma.order.customer.user,model_rma_order,group_rma_customer_user,1,1,1,1
access_rma_user_supplier,rma.order.supplier.user,model_rma_order,group_rma_supplier_user,1,1,1,1
access_rma_manager,rma.order.manager,model_rma_order,group_rma_manager,1,1,1,1
access_rma_line_customer_user,rma.order.line.customer.user,model_rma_order_line,group_rma_customer_user,1,1,1,1
access_rma_line_supplier_user,rma.order.line.customer.supplier,model_rma_order_line,group_rma_supplier_user,1,1,1,1
access_rma_line_manager,rma.order.line,model_rma_order_line,group_rma_manager,1,1,1,1
access_rma_operation_manager,access_rma_operation,model_rma_operation,group_rma_manager,1,1,1,1
access_rma_operation_customer_user,access_rma_operation,model_rma_operation,group_rma_customer_user,1,0,0,0
access_rma_operation_supplier_user,access_rma_operation,model_rma_operation,group_rma_supplier_user,1,0,0,0
access_stock_config_settings,access_stock_config_settings,model_stock_config_settings,stock.group_stock_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_rma_user_customer rma.order.customer.user model_rma_order group_rma_customer_user 1 1 1 1
3 access_rma_user_supplier rma.order.supplier.user model_rma_order group_rma_supplier_user 1 1 1 1
4 access_rma_manager rma.order.manager model_rma_order group_rma_manager 1 1 1 1
5 access_rma_line_customer_user rma.order.line.customer.user model_rma_order_line group_rma_customer_user 1 1 1 1
6 access_rma_line_supplier_user rma.order.line.customer.supplier model_rma_order_line group_rma_supplier_user 1 1 1 1
7 access_rma_line_manager rma.order.line model_rma_order_line group_rma_manager 1 1 1 1
8 access_rma_operation_manager access_rma_operation model_rma_operation group_rma_manager 1 1 1 1
9 access_rma_operation_customer_user access_rma_operation model_rma_operation group_rma_customer_user 1 0 0 0
10 access_rma_operation_supplier_user access_rma_operation model_rma_operation group_rma_supplier_user 1 0 0 0
11 access_stock_config_settings access_stock_config_settings model_stock_config_settings stock.group_stock_manager 1 1 1 1

38
rma/security/rma.xml Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="0">
<record model="ir.module.category" id="module_category_rma">
<field name="name">RMA</field>
<field name="parent_id" ref="base.module_category_sales_management"/>
<field name="sequence">30</field>
</record>
<record id="group_rma_customer_user" model="res.groups">
<field name="name">RMA Customer User</field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="category_id" ref="module_category_rma"/>
</record>
<record id="group_rma_supplier_user" model="res.groups">
<field name="name">RMA Supplier User</field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="category_id" ref="module_category_rma"/>
</record>
<record id="group_rma_manager" model="res.groups">
<field name="name">RMA Manager</field>
<field name="implied_ids"
eval="[(4, ref('rma.group_rma_customer_user')), (4, ref('rma.group_rma_supplier_user'))]"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
<field name="category_id" ref="module_category_rma"/>
</record>
<record id="group_rma_delivery_invoice_address" model="res.groups">
<field name="name">Addresses in RMA</field>
<field name="category_id" ref="module_category_rma"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>
</data>
</odoo>

4
rma/tests/__init__.py Normal file
View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from . import test_rma

242
rma/tests/test_rma.py Normal file
View File

@@ -0,0 +1,242 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp.tests import common
class TestRma(common.TransactionCase):
""" Test the routes and the quantities """
def setUp(self):
super(TestRma, self).setUp()
self.rma_make_picking = self.env['rma_make_picking.wizard']
self.rma_add_invoice = self.env['rma_add_invoice']
self.stockpicking = self.env['stock.picking']
self.rma = self.env['rma.order']
self.rma_line = self.env['rma.order.line']
self.rma_op = self.env['rma.operation']
self.rma_op_id = self.env.ref('rma.rma_operation_customer_replace')
self.product_id = self.env.ref('product.product_product_4')
self.product_1 = self.env.ref('product.product_product_25')
self.product_2 = self.env.ref('product.product_product_30')
self.product_3 = self.env.ref('product.product_product_33')
self.uom_unit = self.env.ref('product.product_uom_unit')
# assign an operation
self.product_1.write({'rma_operation_id': self.rma_op_id.id})
self.product_2.write({'rma_operation_id': self.rma_op_id.id})
self.product_3.write({'rma_operation_id': self.rma_op_id.id})
self.partner_id = self.env.ref('base.res_partner_12')
self.customer_location_id = self.env.ref(
'stock.stock_location_customers'
)
uom_unit = self.env.ref('product.product_uom_unit')
sale_values = self._prepare_sale()
self.sale_order = self.env['sale.order'].create(sale_values)
invoice_id = self.sale_order.action_invoice_create()[0]
self.invoice = self.env['account.invoice'].browse(invoice_id)
# Create the RMA from the invoice
self.rma_id = self.rma.create(
{
'reference': '0001',
'type': 'customer',
'partner_id': self.env.ref('base.res_partner_2').id
})
for line in self.invoice.invoice_line_ids:
data = self.rma_add_invoice.with_context(
{'invoice_id': self.invoice.id}
)._prepare_rma_line_from_inv_line(line)
data.update(rma_id=self.rma_id.id)
self.rma_line.create(data)
#approve the RMA
self.rma_id.action_rma_to_approve()
self.rma_id.action_rma_approve()
def _prepare_sale(self):
values = {
'state': 'done',
'partner_id': self.env.ref('base.res_partner_2').id,
'pricelist_id': self.env.ref('product.list0').id,
'warehouse_id': self.env.ref('stock.warehouse0').id,
'partner_invoice_id': self.env.ref('base.res_partner_2').id,
'partner_shipping_id': self.env.ref('base.res_partner_2').id,
'picking_policy': 'direct',
'order_line': [
(0, False, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': qty,
'qty_delivered': qty,
'product_uom': self.uom_unit.id,
'price_unit': product.list_price
}) for product, qty in [
(self.product_1, 3),
(self.product_2, 5),
(self.product_3, 2),
]
]
}
return values
def test_00_receive_items(self):
wizard = self.rma_make_picking.with_context({
'active_ids': self.rma_id.rma_line_ids.ids,
'active_model': 'rma.order.line',
'picking_type': 'incoming',
'active_id': 1
}).create({})
procurements = wizard._create_picking()
group_ids = set([proc.group_id.id for proc in procurements if
proc.group_id])
domain = "[('group_id','in',[" + ','.join(
map(str, list(group_ids))) + "])]"
picking = self.stockpicking.search(domain)
self.assertEquals(len(picking), 1,
"Incorrect number of pickings created")
moves = picking.move_lines
self.assertEquals(len(moves), 3,
"Incorrect number of moves created")
for line in self.rma_id.rma_line_ids:
self.assertEquals(line.qty_received, 0,
"Wrong qty received")
self.assertEquals(line.qty_to_deliver, 0,
"Wrong qty to deliver")
self.assertEquals(line.qty_outgoing, 0,
"Wrong qty outgoing")
self.assertEquals(line.qty_delivered, 0,
"Wrong qty delivered")
self.assertEquals(line.qty_to_refund, 0,
"Wrong qty to refund")
self.assertEquals(line.qty_to_refunded, 0,
"Wrong qty refunded")
if line.product_id == self.product_1:
self.assertEquals(line.qty_to_receive, 3,
"Wrong qty to receive")
self.assertEquals(line.qty_incoming, 3,
"Wrong qty incoming")
if line.product_id == self.product_2:
self.assertEquals(line.qty_to_receive, 5,
"Wrong qty to receive")
self.assertEquals(line.qty_incoming, 5,
"Wrong qty incoming")
if line.product_id == self.product_3:
self.assertEquals(line.qty_to_receive, 2,
"Wrong qty to receive")
self.assertEquals(line.qty_incoming, 2,
"Wrong qty incoming")
pickings.action_assign()
pickings.do_transfer()
for line in self.rma_id.rma_line_ids:
self.assertEquals(line.qty_to_receive, 0,
"Wrong qty to_receive")
self.assertEquals(line.qty_incoming, 0,
"Wrong qty incoming")
self.assertEquals(line.qty_outgoing, 0,
"Wrong qty outgoing")
self.assertEquals(line.qty_delivered, 0,
"Wrong qty delivered")
self.assertEquals(line.qty_to_refund, 0,
"Wrong qty to refund")
self.assertEquals(line.qty_to_refunded, 0,
"Wrong qty refunded")
if line.product_id == self.product_1:
self.assertEquals(line.qty_received, 3,
"Wrong qty received")
self.assertEquals(line.qty_to_deliver, 3,
"Wrong qty to_deliver")
if line.product_id == self.product_2:
self.assertEquals(line.qty_received, 5,
"Wrong qty received")
self.assertEquals(line.qty_to_deliver, 5,
"Wrong qty to_deliver")
if line.product_id == self.product_3:
self.assertEquals(line.qty_received, 2,
"Wrong qty received")
self.assertEquals(line.qty_to_deliver, 2,
"Wrong qty to_deliver")
wizard = self.rma_make_picking.with_context({
'active_id': 1,
'active_ids': self.rma_id.rma_line_ids.ids,
'active_model': 'rma.order.line',
'picking_type': 'outgoing',
}).create({})
procurements = wizard._create_picking()
group_ids = set([proc.group_id.id for proc in procurements if
proc.group_id])
domain = "[('group_id','in',[" + ','.join(
map(str, list(group_ids))) + "])]"
picking = self.stockpicking.search(domain)
self.assertEquals(len(picking), 1,
"Incorrect number of pickings created")
moves = picking.move_lines
self.assertEquals(len(moves), 3,
"Incorrect number of moves created")
for line in self.rma_id.rma_line_ids:
self.assertEquals(line.qty_to_receive, 0,
"Wrong qty to receive")
self.assertEquals(line.qty_incoming, 0,
"Wrong qty incoming")
self.assertEquals(line.qty_delivered, 0,
"Wrong qty delivered")
self.assertEquals(line.qty_to_refund, 0,
"Wrong qty to refund")
self.assertEquals(line.qty_to_refunded, 0,
"Wrong qty refunded")
if line.product_id == self.product_1:
self.assertEquals(line.qty_to_deliver, 3,
"Wrong qty to deliver")
self.assertEquals(line.qty_outgoing, 3,
"Wrong qty outgoing")
self.assertEquals(line.qty_received, 3,
"Wrong qty received")
if line.product_id == self.product_2:
self.assertEquals(line.qty_received, 5,
"Wrong qty received")
self.assertEquals(line.qty_to_deliver, 5,
"Wrong qty to deliver")
self.assertEquals(line.qty_outgoing, 5,
"Wrong qty outgoing")
if line.product_id == self.product_3:
self.assertEquals(line.qty_received, 2,
"Wrong qty received")
self.assertEquals(line.qty_to_deliver, 2,
"Wrong qty to deliver")
self.assertEquals(line.qty_outgoing, 2,
"Wrong qty outgoing")
pickings.action_assign()
pickings.do_transfer()
for line in self.rma_id.rma_line_ids:
self.assertEquals(line.qty_to_receive, 0,
"Wrong qty to receive")
self.assertEquals(line.qty_incoming, 0,
"Wrong qty incoming")
self.assertEquals(line.qty_to_deliver, 0,
"Wrong qty to deliver")
self.assertEquals(line.qty_to_refund, 0,
"Wrong qty to refund")
self.assertEquals(line.qty_to_refunded, 0,
"Wrong qty refunded")
self.assertEquals(line.qty_outgoing, 0,
"Wrong qty outgoing")
if line.product_id == self.product_1:
self.assertEquals(line.qty_received, 3,
"Wrong qty received")
self.assertEquals(line.qty_delivered, 3,
"Wrong qty delivered")
if line.product_id == self.product_2:
self.assertEquals(line.qty_received, 5,
"Wrong qty received")
self.assertEquals(line.qty_delivered, 5,
"Wrong qty delivered")
if line.product_id == self.product_3:
self.assertEquals(line.qty_received, 2,
"Wrong qty received")
self.assertEquals(line.qty_delivered, 2,
"Wrong qty delivered")
self.rma_id.action_rma_done()

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="procurement_form_view" model="ir.ui.view">
<field name="name">procurement.order.form</field>
<field name="model">procurement.order</field>
<field name="inherit_id"
ref="procurement.procurement_form_view"/>
<field name="arch" type="xml">
<field name="origin" position="after">
<field name="rma_line_id"/>
</field>
</field>
</record>
<record id="procurement_form_view_usage" model="ir.ui.view">
<field name="name">procurement.order.form.stock.inherit</field>
<field name="model">procurement.order</field>
<field name="inherit_id"
ref="stock.view_procurement_form_stock_inherit"/>
<field name="arch" type="xml">
<field name="location_id" position="attributes">
<attribute name="domain">[('usage', '!=', 'view')]</attribute>
</field>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="product_category_form_view" model="ir.ui.view">
<field name="name">product.category.form</field>
<field name="model">product.category</field>
<field name="inherit_id" ref="product.product_category_form_view" />
<field name="arch" type="xml">
<field name="type" position="after">
<field name="rma_operation_id" groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"/>
</field>
</field>
</record>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.stock.property.form.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<group name="inventory" position="inside">
<group name="rma" groups="rma.group_rma_customer_user,rma.group_rma_supplier_user">
<field name="rma_operation_id"/>
</group>
</group>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<odoo>
<data>
<record id="view_company_form" model="ir.ui.view">
<field name="name">base_phone.company.form</field>
<field name="model">res.company</field>
<field name="inherit_id" ref="base.view_company_form" />
<field name="arch" type="xml">
<group name="account_grp" position="after">
<group name="rma" string="RMA">
<field name="rma_rule_id"/>
</group>
</group>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="rma_operation_tree" model="ir.ui.view">
<field name="name">rma.operation.tree</field>
<field name="model">rma.operation</field>
<field name="arch" type="xml">
<tree string="RMA Operations">
<field name="code"/>
<field name="name"/>
<field name="receipt_policy"/>
<field name="delivery_policy"/>
</tree>
</field>
</record>
<record id="rma_operation_form" model="ir.ui.view">
<field name="name">rma.operation.form</field>
<field name="model">rma.operation</field>
<field name="arch" type="xml">
<form string="RMA Operations">
<group colspan="4" col="2">
<group name="description"
string="Description">
<field name="code"/>
<field name="name"/>
<field name="type"/>
</group>
<group name="policies"
string="Policies">
<field name="receipt_policy"/>
<field name="delivery_policy"/>
</group>
<group name="inbound" string="Inbound">
<field name="in_route_id"/>
<field name="in_warehouse_id"/>
<field name="location_id"
domain="[('usage', '=', 'internal')]"/>
<field name="customer_to_supplier"
attrs="{'invisible':[('type', '=', 'customer')]}"/>
</group>
<group name="outbound" string="Outbound">
<field name="out_route_id"/>
<field name="out_warehouse_id"/>
<field name="supplier_to_customer"
attrs="{'invisible':[('type', '=', 'supplier')]}"/>
</group>
</group>
</form>
</field>
</record>
<record id="action_rma_operation" model="ir.actions.act_window">
<field name="name">Operations</field>
<field name="res_model">rma.operation</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="rma_operation_tree"/>
</record>
<menuitem id="menu_rma_operation"
name="Operations"
groups="rma.group_rma_manager"
sequence="35"
parent="rma.menu_rma_config"
action="action_rma_operation"/>
</data>
</odoo>

View File

@@ -0,0 +1,387 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_rma_line_tree" model="ir.ui.view">
<field name="name">rma.order.line.tree</field>
<field name="model">rma.order.line</field>
<field name="arch" type="xml">
<tree string="RMA Line" nocreate="1"
decoration-muted="state in ('draft','to_approve')">
<field name="rma_id"/>
<field name="state" invisible="1"/>
<field name="partner_id"/>
<field name="product_id"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="name"/>
<field name="origin"/>
<field name="operation_id"/>
<field name="supplier_address_id"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="product_qty"/>
<field name="price_unit"/>
</tree>
</field>
</record>
<record id="view_rma_line_supplier_tree" model="ir.ui.view">
<field name="name">rma.order.line.supplier.tree</field>
<field name="model">rma.order.line</field>
<field name="arch" type="xml">
<tree string="RMA Line" nocreate="1"
decoration-muted="state in ('draft','to_approve')">
<field name="rma_id"/>
<field name="state" invisible="1"/>
<field name="partner_id"/>
<field name="product_id"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="name"/>
<field name="origin"/>
<field name="operation_id" domain="[('type','=','supplier')]"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="product_qty"/>
<field name="price_unit"/>
</tree>
</field>
</record>
<record id="view_rma_line_supplier_form" model="ir.ui.view">
<field name="name">rma.order.line.supplier.form</field>
<field name="model">rma.order.line</field>
<field name="arch" type="xml">
<form string="RMA Line" nocreate="1">
<sheet>
<header/>
<div name="button_box" class="oe_button_box">
<button type="object" name="action_view_in_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="in_shipment_count" widget="statinfo"
string="Shipments"/>
</button>
<button type="object" name="action_view_out_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="out_shipment_count" widget="statinfo"
string="Deliveries"/>
</button>
<button type="object" name="action_view_procurements"
class="oe_stat_button"
icon="fa-warning"
groups="stock.group_stock_user">
<field name="procurement_count" widget="statinfo"
string="Proc. Exceptions"/>
</button>
</div>
<div class="oe_title" name="title">
<h1>
<field name="rma_id" required="False"
readonly="True"
invisible="context.get('hide_title',False)"/>
</h1>
</div>
<group>
<group name="product" string="Product">
<field name="product_id"/>
<newline/>
<field name="product_tracking" invisible="1"/>
<field name="lot_id"
groups="stock.group_production_lot"
attrs="{'required': [('product_tracking', 'in', ('serial', 'lot'))]}"/>
</group>
<group name="product_qty" string="Quantity">
<field name="product_qty"/>
<field name="price_unit"/>
<field name="uom_id" groups="product.group_uom"/>
</group>
<newline/>
<group name="operation" string="Operation" colspan="2">
<field name="operation_id"
domain="[('type','=','supplier')]"/>
<field name="receipt_policy"/>
<field name="delivery_policy"/>
</group>
</group>
<notebook>
<page name="description" string="Description">
<field name="description" nolabel="1"/>
</page>
<page name="route" string="Routes" groups="stock.group_adv_location">
<group>
<group>
<group name="inbound" string="Inbound">
<field name="in_warehouse_id"/>
<field name="location_id"
domain="[('usage', '=', 'internal')]"/>
<field name="in_route_id"/>
<field name="supplier_to_customer"/>
</group>
<group name="outbound" string="Outbound">
<field name="out_warehouse_id"/>
<field name="delivery_address_id"
groups='rma.group_rma_delivery_invoice_address'/>
<field name="out_route_id"/>
</group>
</group>
</group>
</page>
<page name="stock" string="Stock Moves">
<field name="move_ids" nolabel="1" readonly="1"/>
</page>
<page name="quantities" string="Quantities">
<group>
<group name="quantities" col="4" string="Quantities">
<group name="receive">
<field name="qty_to_receive"/>
<field name="qty_incoming"/>
<field name="qty_received"/>
</group>
<group name="deliver">
<field name="qty_to_deliver"/>
<field name="qty_outgoing"/>
<field name="qty_delivered"/>
</group>
</group>
</group>
</page>
<page name="other" string="Other Info">
<group name="general" string="General">
<field name="name"/>
<field name="origin"/>
<field name="reference_move_id"/>
<field name="state" invisible="1"/>
</group>
<group name="contact" string="Contact">
<field name="requested_by" readonly="1"/>
<field name="assigned_to" readonly="1"/>
</group>
<group name="company" string="Company"
groups="base.group_multi_company">
<field name="company_id"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<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="arch" type="xml">
<form string="Rma Line" nocreate="1">
<sheet>
<header/>
<div name="button_box" class="oe_button_box">
<button type="object" name="action_view_in_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="in_shipment_count" widget="statinfo"
string="Shipments"/>
</button>
<button type="object" name="action_view_out_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="out_shipment_count" widget="statinfo"
string="Deliveries"/>
</button>
<button type="object" name="action_view_invoice"
class="oe_stat_button"
icon="fa-pencil-square-o"
string="Origin Inv">
</button>
<button type="object" name="action_view_procurements"
class="oe_stat_button"
icon="fa-warning"
groups="stock.group_stock_user">
<field name="procurement_count" widget="statinfo"
string="Exceptions"/>
</button>
</div>
<div class="oe_title" name="title">
<h1>
<field name="rma_id" required="False"
readonly="True"
invisible="context.get('hide_title',False)"/>
</h1>
</div>
<group>
<group name="product" string="Product">
<field name="product_id"/>
<newline/>
<field name="product_tracking" invisible="1"/>
<field name="lot_id"
groups="stock.group_production_lot"
attrs="{'required': [('product_tracking', 'in', ('serial', 'lot'))]}"/>
</group>
<group name="product_qty" string="Quantity">
<field name="product_qty"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="price_unit"/>
</group>
<newline/>
<group name="operation" string="Operation">
<field name="operation_id" domain="[('type','=','customer')]"/>
<field name="receipt_policy"/>
<field name="delivery_policy"/>
</group>
</group>
<notebook>
<page name="route" string="Routes">
<group>
<group>
<group name="inbound" string="Inbound">
<field name="in_warehouse_id"/>
<field name="location_id"
domain="[('usage', '=', 'internal')]"/>
<field name="in_route_id"/>
<field name="customer_to_supplier"/>
<field name="supplier_address_id"
attrs="{'required':[('customer_to_supplier', '=', True)],
'invisible':[('customer_to_supplier', '=', False)]}"/>
</group>
<group name="outbound" string="Outbound">
<field name="out_warehouse_id"/>
<field name="delivery_address_id"
groups='rma.group_rma_delivery_invoice_address'/>
<field name="out_route_id"/>
</group>
</group>
</group>
</page>
<page name="stock" string="Stock Moves">
<field name="move_ids" nolabel="1" readonly="1"/>
</page>
<page name="quantities" string="Quantities">
<group>
<group name="quantities" col="4" string="Quantities">
<group name="receive">
<field name="qty_to_receive"/>
<field name="qty_incoming"/>
<field name="qty_received"/>
</group>
<group name="deliver">
<field name="qty_to_deliver"/>
<field name="qty_outgoing"/>
<field name="qty_delivered"/>
</group>
<group name="supplier_rma"
attrs="{'invisible':[('customer_to_supplier','=',False)]}">
<field name="qty_to_supplier_rma"/>
<field name="qty_in_supplier_rma"/>
</group>
</group>
</group>
</page>
<page name="other" string="Other Info">
<group name="general" string="General">
<field name="name"/>
<field name="origin"/>
<field name="reference_move_id"/>
<field name="state" invisible="1"/>
</group>
<group name="contact" string="Contact">
<field name="requested_by" readonly="1"/>
<field name="assigned_to" readonly="1"/>
</group>
<group name="company" string="Company"
groups="base.group_multi_company">
<field name="company_id"/>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_rma_rma_line_filter" model="ir.ui.view">
<field name="name">rma.order.line.select</field>
<field name="model">rma.order.line</field>
<field name="arch" type="xml">
<search string="Search RMA line">
<field name="rma_id"/>
<field name="partner_id"/>
<field name="requested_by"/>
<field name="assigned_to"/>
<field name="product_id"/>
<field name="lot_id"/>
<separator/>
<filter domain="[('assigned_to','=',uid)]" help="My RMAs"/>
<group expand="0" string="Group By">
<filter name="partner" string="Partner" domain="[]"
context="{'group_by':'partner_id'}"/>
<filter name="operation" string="Operation" domain="[]"
context="{'group_by':'operation_id'}"/>
<filter name="product" string="Product" domain="[]"
context="{'group_by':'product_id'}"/>
<filter name="to_receive" string="To Receive" domain="[('qty_to_receive', '>', 0)]"
context="{'group_by':'partner_id'}"/>
<filter name="to_deliver" string="To Deliver" domain="[('qty_to_deliver', '>', 0)]"
context="{'group_by':'partner_id'}"/>
</group>
</search>
</field>
</record>
<record id="action_rma_customer_lines" model="ir.actions.act_window">
<field name="name">Customer RMA Lines</field>
<field name="res_model">rma.order.line</field>
<field name="domain">[('state','in', ['approved', 'done']),
('type','=', 'customer'),
('customer_to_supplier','=', False)
]</field>
<field name="context">{"search_default_assigned_to":uid}</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_rma_line_tree"/>
</record>
<record id="action_rma_supplier_lines" model="ir.actions.act_window">
<field name="name">Supplier RMA Lines</field>
<field name="res_model">rma.order.line</field>
<field name="domain">[('state','in', ['approved', 'done']),
('type','=', 'supplier')]</field>
<field name="context">{"search_default_assigned_to":uid, "supplier":1}</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_rma_line_supplier_tree"/>
</record>
<record id="action_rma_line_supplier_tree"
model="ir.actions.act_window.view">
<field eval="3" name="sequence"/>
<field name="view_mode">tree</field>
<field name="act_window_id" ref="action_rma_supplier_lines"/>
</record>
<record id="action_rma_line_supplier_form"
model="ir.actions.act_window.view">
<field eval="4" name="sequence"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_rma_line_supplier_form"/>
<field name="act_window_id" ref="action_rma_supplier_lines"/>
</record>
<menuitem
id="menu_rma_line_customer"
sequence="20"
parent="menu_customer_rma"
groups="rma.group_rma_customer_user"
action="action_rma_customer_lines"
/>
<menuitem
id="menu_rma_line_supplier"
sequence="20"
parent="menu_supplier_rma"
groups="rma.group_rma_supplier_user"
action="action_rma_supplier_lines"
/>
</data>
</odoo>

View File

@@ -0,0 +1,365 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_rma_tree" model="ir.ui.view">
<field name="name">rma.order.tree</field>
<field name="model">rma.order</field>
<field name="arch" type="xml">
<tree decoration-info="state == 'draft'"
decoration-muted="state== 'done'" string="RMA">
<field name="name"/>
<field name="state" invisible="1"/>
<field name="reference"/>
<field name="partner_id" groups="base.group_user" string="Customer"/>
<field name="requested_by" groups="base.group_user"/>
<field name="assigned_to" groups="base.group_user"/>
<field name="date_rma"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="view_rma_supplier_tree" model="ir.ui.view">
<field name="name">rma.order.supplier.tree</field>
<field name="model">rma.order</field>
<field name="arch" type="xml">
<tree decoration-info="state == 'draft'"
decoration-muted="state== 'done'" string="RMA">
<field name="name"/>
<field name="state" invisible="1"/>
<field name="reference"/>
<field name="partner_id" groups="base.group_user"
string="Supplier"/>
<field name="requested_by" groups="base.group_user"/>
<field name="assigned_to" groups="base.group_user"/>
<field name="date_rma"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="view_rma_form" model="ir.ui.view">
<field name="name">rma.order.form</field>
<field name="model">rma.order</field>
<field name="arch" type="xml">
<form string="RMA">
<field name="state" invisible="True"/>
<header>
<button name="action_rma_to_approve" type="object"
string="Request Approval"
attrs="{'invisible':[('state', '!=', 'draft')]}"
class="oe_highlight" groups="rma.group_rma_customer_user"/>
<button name="action_rma_draft" type="object"
string="Back to Draft"
attrs="{'invisible':[('state', '=', 'draft')]}" class="oe_highlight"
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"/>
<button name="action_rma_approve" type="object"
string="Approve"
attrs="{'invisible':[('state', '!=', 'to_approve')]}" class="oe_highlight" groups="rma.group_rma_customer_user"/>
<button name="action_rma_done" type="object"
string="Done"
attrs="{'invisible':[('state', 'in', ('done', 'draft'))]}"
groups="rma.group_rma_customer_user"/>
<field name="state" widget="statusbar" nolabel="1"/>
</header>
<sheet name='rma' string="RMA">
<div class="oe_button_box" name="button_box">
<button type="object" name="action_view_in_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="in_shipment_count" widget="statinfo"
string="Shipments"/>
</button>
<button type="object" name="action_view_out_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="out_shipment_count" widget="statinfo"
string="Deliveries"/>
</button>
<button type="object" name="action_view_lines"
class="oe_stat_button"
icon="fa-list"
groups="rma.group_rma_customer_user">
<field name="line_count" widget="statinfo"
string="RMA Lines"/>
</button>
<button type="object" name="action_view_supplier_lines"
class="oe_stat_button"
attrs="{'invisible':[('supplier_line_count', '=', 0)]}"
icon="fa-list"
groups="rma.group_rma_supplier_user">
<field name="supplier_line_count" widget="statinfo"
string="Rel. RMA Lines"/>
</button>
</div>
<div class="oe_title" name="title">
<h1>
<field name="name"/>
</h1>
</div>
<group name="partner">
<field name="partner_id"
domain="[('customer','=',True)]"
string="Customer"
colspan="4"
/>
</group>
<group>
<group col="4">
<field name="assigned_to"
groups="base.group_user"
context="{'default_groups_ref': ['rma.rma_user']}"/>
<field name="reference"/>
<field name="requested_by"
groups="base.group_user"
context="{'default_groups_ref': ['rma.rma_user']}"/>
<field name="date_rma"/>
<field name="type" invisible="1"/>
</group>
</group>
<notebook colspan="4">
<page string="RMA Lines" name="lines">
<field name="rma_line_ids" context="{'default_rma_id': active_id,
'partner_id': partner_id,
'hide_title': True}">
<tree string="RMA Lines" name="lines">
<field name="sequence" widget="handle"/>
<field name="state" invisible="True"/>
<field name="product_id"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="name"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="operation_id"/>
<field name="product_qty"/>
<field name="price_unit"/>
</tree>
</field>
</page>
<page name="other_info" string="Other Info">
<group name="company">
<field name="company_id"
groups="base.group_multi_company"/>
</group>
<group name="comments">
<field name="comment"/>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
<record id="view_rma_supplier_form" model="ir.ui.view">
<field name="name">rma.order.supplier.form</field>
<field name="model">rma.order</field>
<field name="arch" type="xml">
<form string="RMA">
<field name="state" invisible="True"/>
<header>
<button name="action_rma_to_approve" type="object"
string="Request Approval"
attrs="{'invisible':[('state', '!=', 'draft')]}"
class="oe_highlight"
groups="rma.group_rma_supplier_user"/>
<button name="action_rma_draft" type="object"
string="Back to Draft"
attrs="{'invisible':[('state', '=', 'draft')]}" class="oe_highlight"
groups="rma.group_rma_supplier_user"/>
<button name="action_rma_approve" type="object"
string="Approve"
attrs="{'invisible':[('state', '!=', 'to_approve')]}" class="oe_highlight" groups="rma.group_rma_supplier_user"/>
<button name="action_rma_done" type="object"
string="Done"
attrs="{'invisible':[('state', 'in', ('done', 'draft'))]}"
groups="rma.group_rma_manager"/>
<field name="state" widget="statusbar" nolabel="1"/>
</header>
<sheet string="RMA">
<div class="oe_button_box" name="button_box">
<button type="object" name="action_view_in_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="in_shipment_count" widget="statinfo"
string="Shipments"/>
</button>
<button type="object" name="action_view_out_shipments"
class="oe_stat_button"
icon="fa-truck"
groups="stock.group_stock_user">
<field name="out_shipment_count" widget="statinfo"
string="Deliveries"/>
</button>
<button type="object" name="action_view_lines"
class="oe_stat_button"
icon="fa-list"
groups="rma.group_rma_supplier_user">
<field name="line_count" widget="statinfo"
string="RMA Lines"/>
</button>
</div>
<div class="oe_title" name="title">
<h1>
<field name="name"/>
</h1>
</div>
<group name="partner">
<field name="partner_id"
domain="[('supplier','=',True)]"
string="Supplier"/>
</group>
<group>
<group col="4">
<field name="assigned_to"
groups="base.group_user"
context="{'default_groups_ref': ['rma.rma_user']}"/>
<field name="reference"/>
<field name="requested_by"
groups="base.group_user"
context="{'default_groups_ref': ['rma.rma_user']}"/>
<field name="date_rma"/>
<field name="type" invisible="1"/>
</group>
</group>
<notebook colspan="4">
<page string="RMA Lines" name="lines">
<field name="rma_line_ids"
context="{'form_view_ref':'rma.view_rma_line_supplier_form',
'default_rma_id': active_id,
'partner_id': partner_id,
'supplier': 1,
'hide_title': True}">
<tree string="RMA Lines">
<field name="sequence" widget="handle"/>
<field name="state" invisible="True"/>
<field name="name"/>
<field name="product_id"/>
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="name"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="operation_id"/>
<field name="product_qty"/>
<field name="origin"/>
</tree>
</field>
</page>
<page name="other_info" string="Other Info">
<group name="company">
<field name="company_id"
groups="base.group_multi_company"/>
</group>
<group name="comments">
<field name="comment"/>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
<record id="view_rma_rma_filter" model="ir.ui.view">
<field name="name">rma.order.select</field>
<field name="model">rma.order</field>
<field name="arch" type="xml">
<search string="Search RMA">
<field name="reference"/>
<field name="name"/>
<field name="partner_id"/>
<field name="requested_by"/>
<field name="assigned_to"/>
<separator/>
<filter domain="[('assigned_to','=',uid)]" help="My RMAs"/>
<group expand="0" string="Group By">
<filter name="partner" string="Partner" domain="[]"
context="{'group_by':'partner_id'}"/>
<filter name="rma_date" string="RMA Date" domain="[]"
context="{'group_by':'rma_date'}"/>
</group>
</search>
</field>
</record>
<record id="action_rma_customer" model="ir.actions.act_window">
<field name="name">Customer RMA</field>
<field name="res_model">rma.order</field>
<field name="view_type">form</field>
<field name="domain">[('type','=', 'customer')]</field>
<field name="context">{"search_default_assigned_to":uid,
'customer':1}</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_rma_tree"/>
</record>
<record id="action_rma_supplier" model="ir.actions.act_window">
<field name="name">Supplier RMA</field>
<field name="res_model">rma.order</field>
<field name="view_type">form</field>
<field name="domain">[('type','=', 'supplier')]</field>
<field name="context">{"search_default_assigned_to":uid,
'supplier':1}</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_rma_supplier_tree"/>
</record>
<record id="action_rma_supplier_tree"
model="ir.actions.act_window.view">
<field eval="1" name="sequence"/>
<field name="view_mode">tree</field>
<field name="act_window_id" ref="action_rma_supplier"/>
</record>
<record id="action_rma_supplier_form"
model="ir.actions.act_window.view">
<field eval="2" name="sequence"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_rma_supplier_form"/>
<field name="act_window_id" ref="action_rma_supplier"/>
</record>
<menuitem id="menu_customer_rma"
name="Customer RMA"
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
sequence="30"
parent="stock.menu_stock_root"/>
<menuitem id="menu_supplier_rma"
name="Supplier RMA"
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
sequence="31"
parent="stock.menu_stock_root"/>
<menuitem
id="menu_rma_act_customer"
sequence="10"
parent="menu_customer_rma"
groups="rma.group_rma_customer_user"
action="action_rma_customer"/>
<menuitem
id="menu_rma_act_supplier"
sequence="10"
parent="menu_supplier_rma"
groups="rma.group_rma_supplier_user"
action="action_rma_supplier"/>
<menuitem id="menu_rma_config"
name="RMA"
groups="rma.group_rma_manager"
sequence="30"
parent="stock.menu_stock_config_settings"/>
</data>
</odoo>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="rma_rule_tree" model="ir.ui.view">
<field name="name">rma.rule.tree</field>
<field name="model">rma.rule</field>
<field name="arch" type="xml">
<tree string="RMA rules">
<field name="code"/>
<field name="name"/>
</tree>
</field>
</record>
<record id="rma_rule_form" model="ir.ui.view">
<field name="name">rma.rule.form</field>
<field name="model">rma.rule</field>
<field name="arch" type="xml">
<form string="RMA Rules">
<group colspan="4" col="2">
<group name="description"
string="Description">
<field name="code"/>
<field name="name"/>
</group>
<group name="policies"
string="Policies">
<field name="approval_policy"/>
</group>
</group>
</form>
</field>
</record>
<record id="action_rma_rule" model="ir.actions.act_window">
<field name="name">Rules</field>
<field name="res_model">rma.rule</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="rma_rule_tree"/>
</record>
<menuitem id="menu_rma_rule"
name="Rules"
groups="rma.group_rma_manager"
sequence="36"
parent="rma.menu_rma_config"
action="action_rma_rule"/>
</data>
</odoo>

55
rma/views/stock_view.xml Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="view_move_form" model="ir.ui.view">
<field name="name">rma.move.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_form" />
<field name="arch" type="xml">
<group name="destination_grp" position="after">
<group name="rma">
<field name="rma_line_id"/>
</group>
</group>
</field>
</record>
<record id="view_move_picking_form" model="ir.ui.view" >
<field name="name">stock.move.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_picking_form" />
<field name="arch" type="xml">
<xpath expr="//group[1]" position="inside">
<group name="rma" colspan="4">
<field name="rma_line_id"/>
</group>
</xpath>
</field>
</record>
<record id="stock_location_route_form_view_inherit_rma_stock" model="ir.ui.view">
<field name="name">stock.location.route.form</field>
<field name="inherit_id" ref="stock.stock_location_route_form_view"/>
<field name="model">stock.location.route</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='warehouse_ids']" position="before">
<field name="rma_selectable" string="RMA Order Lines"/>
</xpath>
</field>
</record>
<record id="procurement_form_view" model="ir.ui.view">
<field name="name">procurement.order.form.stock.inherit</field>
<field name="model">procurement.order</field>
<field name="inherit_id"
ref="stock.view_procurement_form_stock_inherit"/>
<field name="arch" type="xml">
<field name="location_id" position="attributes">
<attribute name="domain">[('usage', '!=', 'view')]</attribute>
</field>
</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_warehouse_form" model="ir.ui.view">
<field name="name">view_warehouse_form</field>
<field name="model">stock.warehouse</field>
<field name="inherit_id" ref="stock.view_warehouse"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='wh_output_stock_loc_id']" position="after">
<field name="lot_rma_id"/>
</xpath>
<xpath expr="//field[@name='out_type_id']" position="after">
<field name="rma_cust_in_type_id"/>
<field name="rma_cust_out_type_id"/>
<field name="rma_sup_in_type_id"/>
<field name="rma_sup_out_type_id"/>
</xpath>
</field>
</record>
</odoo>

8
rma/wizards/__init__.py Normal file
View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from . import rma_add_stock_move
from . import rma_make_picking
from . import stock_config_settings
from . import rma_order_line_make_supplier_rma

View File

@@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import models, fields, exceptions, api, _
from openerp.exceptions import ValidationError
class RmaAddStockMove(models.TransientModel):
_name = 'rma_add_stock_move'
_description = 'Wizard to add rma lines from pickings'
@api.model
def default_get(self, fields):
res = super(RmaAddStockMove, self).default_get(fields)
rma_obj = self.env['rma.order']
rma_id = self.env.context['active_ids'] or []
active_model = self.env.context['active_model']
if not rma_id:
return res
assert active_model == 'rma.order', 'Bad context propagation'
rma = rma_obj.browse(rma_id)
res['rma_id'] = rma.id
res['partner_id'] = rma.partner_id.id
res['picking_id'] = False
res['move_ids'] = False
return res
rma_id = fields.Many2one('rma.order',
string='RMA Order',
readonly=True,
ondelete='cascade')
partner_id = fields.Many2one(comodel_name='res.partner', string='Partner',
readonly=True)
move_ids = fields.Many2many('stock.move',
string='Stock Moves',
domain="[('state', '=', 'done')]")
def _prepare_rma_line_from_stock_move(self, sm, lot=False):
operation = sm.product_id.rma_operation_id or \
sm.product_id.categ_id.rma_operation_id
data = {
'reference_move_id': sm.id,
'product_id': sm.product_id.id,
'lot_id': lot and lot.id or False,
'name': sm.product_id.name_template,
'origin': sm.picking_id.name,
'uom_id': sm.product_uom.id,
'operation_id': operation.id,
'product_qty': sm.product_uom_qty,
'delivery_address_id': sm.picking_id.partner_id.id,
'rma_id': self.rma_id.id
}
if not operation:
operation = self.env['rma.operation'].search(
[('type', '=', self.rma_id.type)], limit=1)
if not operation:
raise ValidationError("Please define an operation first")
if not operation.in_route_id or not operation.out_route_id:
route = self.env['stock.location.route'].search(
[('rma_selectable', '=', True)], limit=1)
if not route:
raise ValidationError("Please define an rma route")
data.update(
{'in_route_id': operation.in_route_id.id,
'out_route_id': operation.out_route_id.id,
'receipt_policy': operation.receipt_policy,
'operation_id': operation.id,
'refund_policy': operation.refund_policy,
'delivery_policy': operation.delivery_policy
})
if operation.in_warehouse_id:
data['in_warehouse_id'] = operation.in_warehouse_id.id
if operation.out_warehouse_id:
data['out_warehouse_id'] = operation.out_warehouse_id.id
if operation.location_id:
data['location_id'] = operation.location_id.id
return data
@api.model
def _get_existing_stock_moves(self):
existing_move_lines = []
for rma_line in self.rma_id.rma_line_ids:
existing_move_lines.append(rma_line.reference_move_id)
return existing_move_lines
@api.multi
def add_lines(self):
rma_line_obj = self.env['rma.order.line']
existing_stock_moves = self._get_existing_stock_moves()
for sm in self.move_ids:
if sm not in existing_stock_moves:
if sm.lot_ids:
for lot in sm.lot_ids:
data = self._prepare_rma_line_from_stock_move(sm,
lot=lot)
rma_line_obj.with_context(
default_rma_id=self.rma_id.id).create(data)
else:
data = self._prepare_rma_line_from_stock_move(sm, lot=False)
rma_line_obj.with_context(
default_rma_id=self.rma_id.id).create(data)
return {'type': 'ir.actions.act_window_close'}

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_rma_add_stock_move_customer" model="ir.ui.view">
<field name="name">rma.add.stock.move.customer</field>
<field name="model">rma_add_stock_move</field>
<field name="arch" type="xml">
<form string="Select Move">
<group>
<field name="partner_id"
domain="[('customer','=',True)]"
string="Customer"/>
</group>
<separator string="Select Stock Moves to add"/>
<field name="move_ids"
domain="[('picking_id.partner_id', '=', partner_id), ('location_dest_id.usage', '=', 'customer')]">
<tree>
<field name="product_id"/>
<field name="product_uom_qty"/>
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
<field name="product_packaging" domain="[('product_tmpl_id','=',product_tmpl_id)]" groups="product.group_stock_packaging"/>
<field name="picking_id"/>
<field name="location_id" groups="stock.group_locations"/>
<field name="location_dest_id" groups="stock.group_locations"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="date" string="Date" groups="base.group_no_one"/>
<field name="date_expected" string="Date Expected"/>
<field name="state"/>
</tree>
</field>
<footer>
<button
string="Confirm"
name="add_lines" 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_add_stock_move_customer"
model="ir.actions.act_window">
<field name="name">Add from Stock Move</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">rma_add_stock_move</field>
<field name="src_model">rma.order</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="view_id" ref="view_rma_add_stock_move_customer"/>
<field name="groups_id"
eval="[(4, ref('rma.group_rma_customer_user'))]"/>
</record>
<record id="view_rma_add_stock_move_customer_form" model="ir.ui.view">
<field name="name">rma.order.line.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_rma_done" position="after">
<button name="%(action_rma_add_stock_move_customer)d"
string="Add From Stock Move"
type="action"
states="draft,to_approve"/>
</button>
</field>
</record>
<record id="view_rma_add_stock_move_supplier" model="ir.ui.view">
<field name="name">rma.add.stock.move.supplier</field>
<field name="model">rma_add_stock_move</field>
<field name="arch" type="xml">
<form string="Select Move">
<group>
<field name="partner_id"
domain="[('supplier','=',True)]"
string="Supplier"/>
</group>
<separator string="Select Stock Moves to add"/>
<field name="move_ids"
domain="[('picking_id.partner_id', '=', partner_id), ('location_dest_id.usage', '=', 'supplier')]"/>
<footer>
<button
string="Confirm"
name="add_lines" 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_add_stock_move_supplier"
model="ir.actions.act_window">
<field name="name">Add from Stock Move</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">rma_add_stock_move</field>
<field name="src_model">rma.order</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="view_id" ref="view_rma_add_stock_move_supplier"/>
<field name="groups_id"
eval="[(4, ref('rma.group_rma_supplier_user'))]"/>
</record>
<record id="view_rma_add_stock_move_button_supplier_form"
model="ir.ui.view">
<field name="name">rma.order.line.supplier.form</field>
<field name="model">rma.order</field>
<field name="inherit_id" ref="rma.view_rma_supplier_form"/>
<field name="arch" type="xml">
<button name="action_rma_done" position="after">
<button name="%(action_rma_add_stock_move_supplier)d"
string="Add From Stock Move"
type="action"
states="draft,to_approve"/>
</button>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,233 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
import time
from openerp import models, fields, exceptions, api, _
from openerp.exceptions import ValidationError
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT
import openerp.addons.decimal_precision as dp
class RmaMakePicking(models.TransientModel):
_name = 'rma_make_picking.wizard'
_description = 'Wizard to create pickings from rma lines'
@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.id,
'wiz_id': self.env.context['active_id'],
}
return values
@api.model
def default_get(self, fields):
"""Default values for wizard, if there is more than one supplier on
lines the supplier field is empty otherwise is the unique line
supplier.
"""
res = super(RmaMakePicking, self).default_get(fields)
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 exceptions.Warning(
_('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
return res
item_ids = fields.One2many(
'rma_make_picking.wizard.item',
'wiz_id', string='Items')
def find_procurement_group(self, item):
return self.env['procurement.group'].search([('rma_id', '=',
item.line_id.rma_id.id)])
def _get_procurement_group_data(self, item):
group_data = {
'name': item.line_id.rma_id.name,
'rma_id': item.line_id.rma_id.id,
}
return group_data
@api.model
def _get_address(self, item):
if item.line_id.delivery_address_id:
delivery_address = item.line_id.delivery_address_id or \
item.line_id.partner_id
elif item.line_id.customer_to_supplier:
delivery_address = item.line_id.supplier_address_id
else:
raise exceptions.Warning('Unknown delivery address')
return delivery_address
def _get_address_location(self, delivery_address_id, type):
if type == 'supplier':
return delivery_address_id.property_stock_supplier
elif 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)
if picking_type == 'incoming':
if line.customer_to_supplier:
location = self._get_address_location(
delivery_address_id, 'supplier')
else:
location = line.location_id
warehouse = line.in_warehouse_id
route = line.in_route_id
else:
location = self._get_address_location(delivery_address_id,
line.rma_id.type)
warehouse = line.out_warehouse_id
route = line.out_route_id
if not route:
raise exceptions.Warning("No route specified")
if not warehouse:
raise exceptions.Warning("No warehouse specified")
procurement_data = {
'name': line.rma_id.name,
'group_id': group.id,
'origin': line.rma_id.name,
'warehouse_id': warehouse.id,
'date_planned': time.strftime(DT_FORMAT),
'product_id': item.product_id.id,
'product_qty': qty,
'partner_dest_id': delivery_address_id.id,
'product_uom': line.product_id.product_tmpl_id.uom_id.id,
'location_id': location.id,
'rma_line_id': line.id,
'route_ids': [(4, route.id)]
}
return procurement_data
@api.model
def _create_procurement(self, item, picking_type):
group = self.find_procurement_group(item)
if not group:
procurement_group = self._get_procurement_group_data(item)
group = self.env['procurement.group'].create(procurement_group)
if picking_type == 'incoming':
qty = item.qty_to_receive
else:
qty = item.qty_to_deliver
procurement_data = self._get_procurement_data(
item, group, qty, picking_type)
# create picking
procurement = self.env['procurement.order'].create(procurement_data)
procurement.run()
return procurement.id
@api.multi
def _create_picking(self):
"""Method called when the user clicks on create picking"""
picking_type = self.env.context.get('picking_type')
procurement_list = []
for item in self.item_ids:
line = item.line_id
if line.state != 'approved':
raise exceptions.Warning(
_('RMA %s is not approved') %
line.rma_id.name)
if line.receipt_policy == 'no' and picking_type == \
'incoming':
raise exceptions.Warning(
_('No shipments needed for this operation'))
if line.delivery_policy == 'no' and picking_type == \
'outgoing':
raise exceptions.Warning(
_('No deliveries needed for this operation'))
procurement = self._create_procurement(item, picking_type)
procurement_list.append(procurement)
procurements = self.env['procurement.order'].browse(procurement_list)
return procurements
@api.model
def _get_action(self, pickings, procurements):
if pickings:
action = procurements.do_view_pickings()
else:
action = self.env.ref(
'procurement.procurement_order_action_exceptions')
action = action.read()[0]
# choose the view_mode accordingly
procurement_ids = procurements.ids
if len(procurement_ids) != 1:
action['domain'] = "[('id', 'in', " + \
str(procurement_ids) + ")]"
elif len(procurements) == 1:
res = self.env.ref('procurement.procurement_form_view',
False)
action['views'] = [(res and res.id or False, 'form')]
action['res_id'] = procurement_ids[0]
return action
@api.multi
def action_create_picking(self):
procurements = self._create_picking()
groups = []
for proc in procurements:
if proc.group_id:
groups.append(proc.group_id.id)
if len(groups):
pickings =self.env['stock.picking'].search(
[('group_id', 'in', groups)])
action = self._get_action(pickings, procurements)
return action
@api.multi
def action_cancel(self):
return {'type': 'ir.actions.act_window_close'}
class RmaMakePickingItem(models.TransientModel):
_name = "rma_make_picking.wizard.item"
_description = "Items to receive"
wiz_id = fields.Many2one(
'rma_make_picking.wizard',
string='Wizard', required=True)
line_id = fields.Many2one('rma.order.line',
string='RMA order Line',
readonly=True,
ondelete='cascade')
rma_id = fields.Many2one('rma.order',
related='line_id.rma_id',
string='RMA',
readonly=True)
product_id = fields.Many2one('product.product', string='Product',
readonly=True)
product_qty = fields.Float(
related='line_id.product_qty',
string='Quantity Ordered', copy=False,
digits=dp.get_precision('Product Unit of Measure'), readonly=True)
qty_to_receive = fields.Float(
string='Quantity to Receive',
digits=dp.get_precision('Product Unit of Measure'))
qty_to_deliver = fields.Float(
string='Quantity To Deliver',
digits=dp.get_precision('Product Unit of Measure'))
uom_id = fields.Many2one('product.uom', string='Unit of Measure',
readonly=True)

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_rma_picking" model="ir.ui.view">
<field name="name">rma_picking</field>
<field name="model">rma_make_picking.wizard</field>
<field name="arch" type="xml">
<form string="Select lines for picking" name="lines">
<separator string="Select lines for picking"/>
<field name="item_ids">
<tree string="RMA Lines" editable="bottom" create="false">
<field name="rma_id"/>
<field name="product_id"/>
<field name="product_qty"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="qty_to_receive" readonly="0"/>
</tree>
</field>
<footer>
<button
string="Confirm"
name="action_create_picking" type="object"
class="oe_highlight"/>
or
<button name="action_cancel"
string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="view_rma_picking_out" model="ir.ui.view">
<field name="name">Create Delivery</field>
<field name="model">rma_make_picking.wizard</field>
<field name="arch" type="xml">
<form string="Select lines for picking" name="lines">
<separator string="Select lines for picking"/>
<field name="item_ids">
<tree string="RMA Lines" editable="bottom" create="false">
<field name="rma_id"/>
<field name="product_id"/>
<field name="product_qty"/>
<field name="uom_id" groups="product.group_uom"/>
<field name="qty_to_deliver" readonly="0"/>
</tree>
</field>
<footer>
<button
string="Confirm"
name="action_create_picking" 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_picking_in" model="ir.actions.act_window">
<field name="name">Create Incoming Shipment</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">rma_make_picking.wizard</field>
<field name="src_model">rma.order.line</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="view_id" ref="view_rma_picking"/>
<field name="groups_id" eval="[(4, ref('stock.group_stock_user'))]"/>
<field name="context">{'picking_type': 'incoming'}</field>
</record>
<record id="action_rma_picking_out" model="ir.actions.act_window">
<field name="name">Create Delivery</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">rma_make_picking.wizard</field>
<field name="src_model">rma.order.line</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="view_id" ref="view_rma_picking_out"/>
<field name="groups_id" eval="[(4, ref('stock.group_stock_user'))]"/>
<field name="context">{'picking_type': 'outgoing'}</field>
</record>
<record id="view_rma_line_button_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">
<header position="inside">
<button name="%(action_rma_picking_in)d" states="approved"
string="Create Incoming Shipment" class="oe_highlight"
type="action"/>
<button name="%(action_rma_picking_out)d" states="approved"
string="Create Delivery" class="oe_highlight"
type="action"/>
</header>
</field>
</record>
<record id="view_rma_line_supplier_button_form" model="ir.ui.view">
<field name="name">rma.order.line.supplier.form</field>
<field name="model">rma.order.line</field>
<field name="inherit_id" ref="rma.view_rma_line_supplier_form"/>
<field name="arch" type="xml">
<div name="button_box" position="before">
<header>
<button name="%(action_rma_picking_in)d" states="approved"
string="Create Incoming Shipment" class="oe_highlight"
type="action"/>
<button name="%(action_rma_picking_out)d" states="approved"
string="Create Delivery" class="oe_highlight"
type="action"/>
</header>
</div>
</field>
</record>
<record model="ir.values" id="values_action_rma_picking_in">
<field name="model_id" ref="model_rma_order_line"/>
<field name="name">Create Incoming Shipment</field>
<field name="key2">client_action_multi</field>
<field name="value" eval="'ir.actions.act_window,' + str(ref('action_rma_picking_in'))" />
<field name="key">action</field>
<field name="model">rma.order.line</field>
</record>
<record model="ir.values" id="values_action_rma_picking_out">
<field name="model_id" ref="model_rma_order_line"/>
<field name="name">Create Delivery</field>
<field name="key2">client_action_multi</field>
<field name="value"
eval="'ir.actions.act_window,' + str(ref('action_rma_picking_out'))" />
<field name="key">action</field>
<field name="model">rma.order.line</field>
</record>
</odoo>

View File

@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
import openerp.addons.decimal_precision as dp
from openerp import _, api, exceptions, fields, models
class RmaLineMakeSupplierRma(models.TransientModel):
_name = "rma.order.line.make.supplier.rma"
_description = "RMA Line Make Supplier RMA"
partner_id = fields.Many2one('res.partner', string='Supplier',
required=False,
domain=[('supplier', '=', True)])
item_ids = fields.One2many(
'rma.order.line.make.supplier.rma.item',
'wiz_id', string='Items')
supplier_rma_id = fields.Many2one('rma.order',
string='Supplier RMA Order',
required=False,
domain=[('state', '=', 'draft')])
@api.model
def _prepare_item(self, line):
return {
'line_id': line.id,
'product_id': line.product_id.id,
'name': line.name,
'product_qty': line.qty_to_supplier_rma,
'uom_id': line.uom_id.id,
}
@api.model
def default_get(self, fields):
res = super(RmaLineMakeSupplierRma, self).default_get(
fields)
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)])
suppliers = lines.mapped('supplier_address_id')
if len(suppliers) == 1:
res['partner_id'] = suppliers.id
else:
raise exceptions.Warning(
_('Only RMA lines from the same supplier address can be '
'processed at the same time'))
res['item_ids'] = items
return res
@api.model
def _prepare_supplier_rma(self, company):
if not self.partner_id:
raise exceptions.Warning(
_('Enter a supplier.'))
return {
'partner_id': self.partner_id.id,
'delivery_address_id': self.partner_id.id,
'type': 'supplier',
'company_id': company.id,
}
@api.model
def _prepare_supplier_rma_line(self, rma, item):
operation = self.env['rma.operation'].search(
[('type', '=', 'supplier')], limit=1)
return {
'origin': item.line_id.rma_id.name,
'delivery_address_id':
item.line_id.delivery_address_id.id,
'product_id': item.line_id.product_id.id,
'customer_rma_id': item.line_id.id,
'product_qty': item.product_qty,
'rma_id': rma.id,
'operation_id': operation.id,
'receipt_policy': operation.receipt_policy,
'delivery_policy': operation.delivery_policy,
'in_warehouse_id': operation.in_warehouse_id.id,
'out_warehouse_id': operation.out_warehouse_id.id,
'location_id': operation.location_id.id,
'supplier_to_customer': operation.supplier_to_customer,
'in_route_id': operation.in_route_id.id,
'out_route_id': operation.out_route_id.id,
}
@api.multi
def make_supplier_rma(self):
res = []
rma_obj = self.env['rma.order']
rma_line_obj = self.env['rma.order.line']
rma = False
for item in self.item_ids:
line = item.line_id
if item.product_qty <= 0.0:
raise exceptions.Warning(
_('Enter a positive quantity.'))
if self.supplier_rma_id:
rma = self.supplier_rma_id
if not rma:
rma_data = self._prepare_supplier_rma(line.company_id)
rma = rma_obj.create(rma_data)
rma_line_data = self._prepare_supplier_rma_line(rma, item)
rma_line_obj.create(rma_line_data)
res.append(rma.id)
return {
'domain': "[('id','in', ["+','.join(map(str, res))+"])]",
'name': _('Supplier RMA'),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'rma.order',
'view_id': False,
'context': {'supplier': 1},
'type': 'ir.actions.act_window'
}
class RmaLineMakeRmaOrderItem(models.TransientModel):
_name = "rma.order.line.make.supplier.rma.item"
_description = "RMA Line Make Supplier RMA Item"
wiz_id = fields.Many2one(
'rma.order.line.make.supplier.rma',
string='Wizard', required=True, ondelete='cascade',
readonly=True)
line_id = fields.Many2one('rma.order.line',
string='RMA Line',
required=True)
rma_id = fields.Many2one('rma.order', related='line_id.rma_id',
string='RMA Order', readonly=True)
product_id = fields.Many2one('product.product',
related='line_id.product_id', readony=True)
name = fields.Char(related='line_id.name', readonly=True)
uom_id = fields.Many2one('product.uom', string='UoM', readonly=True)
product_qty = fields.Float(string='Quantity to sell',
digits=dp.get_precision('Product UoS'))

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2016 Eficent Business and IT Consulting Services S.L.
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
<openerp>
<data>
<record id="view_rma_order_line_make_supplier_rma"
model="ir.ui.view">
<field name="name">RMA Line Make Supplier RMA</field>
<field name="model">rma.order.line.make.supplier.rma</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Create Supplier RMA">
<separator string="Existing Supplier RMA to update:"/>
<newline/>
<group>
<field name="supplier_rma_id"/>
</group>
<newline/>
<separator
string="New RMA details:"/>
<newline/>
<group>
<field name="partner_id"/>
</group>
<newline/>
<group>
<field name="item_ids" nolabel="1" colspan="2">
<tree string="Details" editable="bottom" create="false">
<field name="line_id"
options="{'no_open': true}"/>
<field name="product_id"/>
<field name="name"/>
<field name="product_qty"/>
<field name="uom_id"
groups="product.group_uom"/>
</tree>
</field>
</group>
<newline/>
<group colspan="2">
<button name="make_supplier_rma"
string="Create/Update Supplier RMA"
type="object"
class="oe_highlight"/>
<button special="cancel" string="Cancel" class="oe_link"/>
</group>
</form>
</field>
</record>
<record id="action_rma_order_line_make_supplier_rma"
model="ir.actions.act_window">
<field name="name">Create Supplier RMA</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">rma.order.line.make.supplier.rma</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_rma_order_line_make_supplier_rma"/>
<field name="target">new</field>
</record>
<record model="ir.values" id="rma_order_line_make_supplier_rma">
<field name="model_id" ref="model_rma_order_line" />
<field name="name">Create Supplier RMA</field>
<field name="key2">client_action_multi</field>
<field name="value"
eval="'ir.actions.act_window,' + str(ref('action_rma_order_line_make_supplier_rma'))" />
<field name="key">action</field>
<field name="model">rma.order.line</field>
</record>
<record id="view_rma_line_supplier_rma_button_form" model="ir.ui.view">
<field name="name">rma.order.line.supplier.rma.form</field>
<field name="model">rma.order.line</field>
<field name="inherit_id" ref="rma.view_rma_line_form"/>
<field name="arch" type="xml">
<header position="inside">
<button name="%(action_rma_order_line_make_supplier_rma)d"
states="approved"
string="Create Supplier RMA" class="oe_highlight"
type="action"/>
</header>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from openerp import models, fields
class StockConfigSettings(models.Model):
_inherit = 'stock.config.settings'
group_rma_delivery_address = fields.Selection([
(0, "Invoicing and shipping addresses are always the same "
"(Example: services companies)"),
(1, 'Display 3 fields on rma: partner, invoice address, delivery '
'address')
], "Addresses",
implied_group='rma.group_rma_delivery_invoice_address')

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_stock_config_settings_rma" model="ir.ui.view">
<field name="name">stock.config.settings.rma</field>
<field name="model">stock.config.settings</field>
<field name="inherit_id" ref="stock.view_stock_config_settings"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='shipping']" position="after">
<group string="Return Merchandise Authorization">
<field name="group_rma_delivery_address" widget="radio"/>
</group>
</xpath>
</field>
</record>
</data>
</openerp>