mirror of
https://github.com/ForgeFlow/stock-rma.git
synced 2025-01-21 12:57:49 +02:00
Merge pull request #512 from ForgeFlow/17.0-mig-rma-good
[17.0][MIG] rma
This commit is contained in:
160
rma/README.rst
Normal file
160
rma/README.rst
Normal file
@@ -0,0 +1,160 @@
|
||||
======================================
|
||||
RMA (Return Merchandise Authorization)
|
||||
======================================
|
||||
|
||||
..
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:310ce4e3f5919e5be643c54f11481d09bb8fc79cab65f08ac969ef3bf9d799fe
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
:target: https://odoo-community.org/page/development-status
|
||||
:alt: Beta
|
||||
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
|
||||
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
|
||||
:alt: License: LGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-ForgeFlow%2Fstock--rma-lightgray.png?logo=github
|
||||
:target: https://github.com/ForgeFlow/stock-rma/tree/17.0/rma
|
||||
:alt: ForgeFlow/stock-rma
|
||||
|
||||
|badge1| |badge2| |badge3|
|
||||
|
||||
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.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
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.
|
||||
|
||||
RMA Approval Policy
|
||||
-------------------
|
||||
|
||||
There are two RMA approval policies in product catogories:
|
||||
|
||||
- One step: Always auto-approve RMAs that only contain products within
|
||||
categories with this policy.
|
||||
- Two steps: A RMA order containing a product within a category with
|
||||
this policy will request the RMA manager approval.
|
||||
|
||||
In order to change the approval policy of a product category follow the
|
||||
next steps:
|
||||
|
||||
1. Go to *Inventory > Configuration > Products > Product Categories*.
|
||||
2. Select one and change the field *RMA Approval Policy* to your
|
||||
convenience.
|
||||
|
||||
Other Settings
|
||||
--------------
|
||||
|
||||
1. Go to RMA > Configuration > Settings > Return Merchandising
|
||||
Authorization and select the option "Display 3 fields on rma:
|
||||
partner, invoice address, delivery address" if needed.
|
||||
2. Go to RMA > 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 type of 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:
|
||||
|
||||
1. Select a partner. Enter RMA lines associated to an existing picking,
|
||||
or manually.
|
||||
2. Request approval and approve.
|
||||
3. Click on RMA Lines button.
|
||||
4. Click on more and select an option: "Receive products", "Create
|
||||
Delivery Order".
|
||||
5. Go back to the RMA. Set the RMA to done if not further action is
|
||||
required.
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
- Picking operations report in customer RMA dropshipping case is
|
||||
showing "Vendor Address" while it should be "Customer Address".
|
||||
- Dropshipping always counted as a delivery on the smart buttons.
|
||||
- Uninstall hook.
|
||||
- Constraints instead of required fields on rma.order.line.
|
||||
- Rename type field on rma.order and rma.order.line
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/ForgeFlow/stock-rma/issues>`_.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
`feedback <https://github.com/ForgeFlow/stock-rma/issues/new?body=module:%20rma%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
* ForgeFlow
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
- Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
|
||||
- Aaron Henriquez <ahenriquez@forgeflow.com>
|
||||
- Lois Rilo <lois.rilo@forgeflow.com>
|
||||
- Bhavesh Odedra <bodedra@opensourceintegrators.com>
|
||||
- Akim Juillerat <akim.juillerat@camptocamp.com>
|
||||
- Alexandre Fayolle <alexandre.fayolle@camptocamp.com>
|
||||
- Chafique Delli <chafique.delli@akretion.com>
|
||||
- Héctor Villarreal <hector.villarreal@forgeflow.com>
|
||||
|
||||
Maintainers
|
||||
-----------
|
||||
|
||||
This module is part of the `ForgeFlow/stock-rma <https://github.com/ForgeFlow/stock-rma/tree/17.0/rma>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute.
|
||||
5
rma/__init__.py
Normal file
5
rma/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from . import models
|
||||
from . import wizards
|
||||
39
rma/__manifest__.py
Normal file
39
rma/__manifest__.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
{
|
||||
"name": "RMA (Return Merchandise Authorization)",
|
||||
"version": "17.0.1.0.0",
|
||||
"license": "LGPL-3",
|
||||
"category": "RMA",
|
||||
"summary": "Introduces the return merchandise authorization (RMA) process in odoo",
|
||||
"author": "ForgeFlow",
|
||||
"website": "https://github.com/ForgeFlow",
|
||||
"depends": ["stock", "mail", "web"],
|
||||
"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",
|
||||
"report/rma_report.xml",
|
||||
"report/rma_report_templates.xml",
|
||||
"views/rma_order_view.xml",
|
||||
"views/rma_operation_view.xml",
|
||||
"views/rma_order_line_view.xml",
|
||||
"views/stock_view.xml",
|
||||
"views/stock_warehouse.xml",
|
||||
"views/product_view.xml",
|
||||
"views/res_partner_view.xml",
|
||||
"views/res_config_settings_views.xml",
|
||||
"views/rma_menu.xml",
|
||||
"wizards/rma_make_picking_view.xml",
|
||||
"wizards/rma_add_stock_move_view.xml",
|
||||
"wizards/rma_order_line_make_supplier_rma_view.xml",
|
||||
"report/report_deliveryslip.xml",
|
||||
"wizards/rma_add_serial_views.xml",
|
||||
],
|
||||
"installable": True,
|
||||
"application": True,
|
||||
}
|
||||
45
rma/data/rma_operation.xml
Normal file
45
rma/data/rma_operation.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo noupdate="1">
|
||||
<record id="rma_operation_customer_replace" model="rma.operation">
|
||||
<field name="name">Replace After Receive</field>
|
||||
<field name="code">RPL-C</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">RPL-S</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_ds_replace" model="rma.operation">
|
||||
<field name="name">Dropship - Deliver to vendor</field>
|
||||
<field name="code">DS-RPL-C</field>
|
||||
<field name="receipt_policy">ordered</field>
|
||||
<field name="delivery_policy">no</field>
|
||||
<field name="type">customer</field>
|
||||
<field name="customer_to_supplier" eval="True" />
|
||||
<field name="out_route_id" ref="rma.route_rma_dropship" />
|
||||
<field name="in_route_id" ref="rma.route_rma_dropship" />
|
||||
</record>
|
||||
|
||||
<record id="rma_operation_ds_replace_supplier" model="rma.operation">
|
||||
<field name="name">Dropship - Deliver to customer</field>
|
||||
<field name="code">DS-RPL-S</field>
|
||||
<field name="receipt_policy">no</field>
|
||||
<field name="delivery_policy">ordered</field>
|
||||
<field name="type">supplier</field>
|
||||
<field name="supplier_to_customer" eval="True" />
|
||||
<field name="out_route_id" ref="rma.route_rma_dropship" />
|
||||
<field name="in_route_id" ref="rma.route_rma_dropship" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
42
rma/data/rma_sequence.xml
Normal file
42
rma/data/rma_sequence.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" ?>
|
||||
<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">RMAG/%(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">RTVG/%(year)s/</field>
|
||||
<field eval="1" name="number_next" />
|
||||
<field eval="1" name="number_increment" />
|
||||
<field name="company_id" eval="False" />
|
||||
</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">RMA/%(year)s/</field>
|
||||
<field eval="1" name="number_next" />
|
||||
<field eval="1" name="number_increment" />
|
||||
<field name="company_id" eval="False" />
|
||||
</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">RTV/%(year)s/</field>
|
||||
<field eval="1" name="number_next" />
|
||||
<field eval="1" name="number_increment" />
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
206
rma/data/stock_data.xml
Normal file
206
rma/data/stock_data.xml
Normal file
@@ -0,0 +1,206 @@
|
||||
<?xml version="1.0" ?>
|
||||
<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="sequence_code">RMA → Customer</field>
|
||||
<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="sequence_code">Customer → RMA</field>
|
||||
<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="sequence_code">RMA -> Supplier</field>
|
||||
<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="sequence_code">Supplier -> RMA</field>
|
||||
<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="sequence_code">Customer -> Supplier</field>
|
||||
<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="sequence_code">Supplier -> Customer</field>
|
||||
<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.route'>
|
||||
<field name="name">RMA Customer</field>
|
||||
<field name="company_id" eval="0" />
|
||||
<field name="sequence">10</field>
|
||||
<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.route'>
|
||||
<field name="name">RMA Supplier</field>
|
||||
<field name="company_id" eval="0" />
|
||||
<field name="sequence">10</field>
|
||||
<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.route'>
|
||||
<field name="name">RMA Dropship</field>
|
||||
<field name="company_id" eval="0" />
|
||||
<field name="sequence">10</field>
|
||||
<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="stock.rule">
|
||||
<field name="name">Customer → RMA</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="warehouse_id" ref="stock.warehouse0" />
|
||||
<field name="location_src_id" ref="stock.stock_location_customers" />
|
||||
<field name="location_dest_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="stock.rule">
|
||||
<field name="name">RMA → Customer</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="warehouse_id" ref="stock.warehouse0" />
|
||||
<field name="location_src_id" ref="rma.location_rma" />
|
||||
<field name="location_dest_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="stock.rule">
|
||||
<field name="name">RMA → Supplier</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="warehouse_id" ref="stock.warehouse0" />
|
||||
<field name="location_dest_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="stock.rule">
|
||||
<field name="name">Supplier → RMA</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="warehouse_id" ref="stock.warehouse0" />
|
||||
<field name="location_dest_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="stock.rule">
|
||||
<field name="name">Customer → Supplier</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="location_dest_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="stock.rule">
|
||||
<field name="name">Supplier → Customer</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="location_dest_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>
|
||||
115
rma/demo/stock_demo.xml
Normal file
115
rma/demo/stock_demo.xml
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo noupdate="1">
|
||||
|
||||
<!--Sequences-->
|
||||
<record id="seq_picking_type_rma_cust_out" model="ir.sequence">
|
||||
<field name="name">RMA Customer Sequence out</field>
|
||||
<field name="prefix">RMA/CUST/OUT</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">RMA Customer Sequence in</field>
|
||||
<field name="prefix">RMA/CUST/IN</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">RMA Supplier Sequence out</field>
|
||||
<field name="prefix">RMA/SUPP/OUT</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">RMA Supplier Sequence in</field>
|
||||
<field name="prefix">RMA/SUPP/IN</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">RMA Dropship</field>
|
||||
<field name="prefix">RMA/DS</field>
|
||||
<field name="padding">5</field>
|
||||
<field name="company_id" eval="False" />
|
||||
</record>
|
||||
|
||||
<!--Dropshipping Types-->
|
||||
<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>
|
||||
|
||||
<!--Routes-->
|
||||
<record id="route_rma_customer" model="stock.route">
|
||||
<field name="name">RMA Customer</field>
|
||||
<field name="sequence">10</field>
|
||||
<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.route">
|
||||
<field name="name">RMA Supplier</field>
|
||||
<field name="sequence">10</field>
|
||||
<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.route">
|
||||
<field name="name">RMA Dropship</field>
|
||||
<field name="sequence">10</field>
|
||||
<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>
|
||||
|
||||
<!--Dropship rules-->
|
||||
<record id="rule_rma_dropship_return" model="stock.rule">
|
||||
<field name="name">Customer → Supplier</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="warehouse_id" ref="stock.warehouse0" />
|
||||
<field name="location_dest_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="stock.rule">
|
||||
<field name="name">Supplier → Customer</field>
|
||||
<field name="action">pull</field>
|
||||
<field name="warehouse_id" ref="stock.warehouse0" />
|
||||
<field name="location_dest_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>
|
||||
|
||||
<!-- Enable the RMA in warehouse0 -->
|
||||
<record id="stock.warehouse0" model="stock.warehouse">
|
||||
<field name="rma_in_this_wh" eval="True" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
1785
rma/i18n/es.po
Normal file
1785
rma/i18n/es.po
Normal file
File diff suppressed because it is too large
Load Diff
1779
rma/i18n/rma.pot
Normal file
1779
rma/i18n/rma.pot
Normal file
File diff suppressed because it is too large
Load Diff
13
rma/models/__init__.py
Normal file
13
rma/models/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from . import rma_order
|
||||
from . import rma_order_line
|
||||
from . import rma_operation
|
||||
from . import stock_move
|
||||
from . import stock_quant_package
|
||||
from . import stock_warehouse
|
||||
from . import product
|
||||
from . import product_category
|
||||
from . import procurement_group
|
||||
from . import stock_rule
|
||||
from . import res_partner
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
37
rma/models/procurement_group.py
Normal file
37
rma/models/procurement_group.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2017-22 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class ProcurementGroup(models.Model):
|
||||
_inherit = "procurement.group"
|
||||
|
||||
rma_id = fields.Many2one(
|
||||
comodel_name="rma.order", string="RMA", ondelete="set null"
|
||||
)
|
||||
rma_line_id = fields.Many2one(
|
||||
comodel_name="rma.order.line", string="RMA line", ondelete="set null"
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _get_rule(self, product_id, location_id, values):
|
||||
"""Ensure that the selected rule is valid for RMAs"""
|
||||
res = super()._get_rule(product_id, location_id, values)
|
||||
rma_route_check = self.env.context.get("rma_route_check")
|
||||
if rma_route_check:
|
||||
if res and not res.route_id.rma_selectable:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"No rule found for this product %(product)s and "
|
||||
"location %(location)s that is valid for RMA operations."
|
||||
)
|
||||
% {
|
||||
"product": product_id.default_code or product_id.name,
|
||||
"location": location_id.complete_name,
|
||||
}
|
||||
)
|
||||
# Don't enforce check on any chained moves
|
||||
rma_route_check.clear()
|
||||
return res
|
||||
22
rma/models/product.py
Normal file
22
rma/models/product.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ProductTemplate(models.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
rma_customer_operation_id = fields.Many2one(
|
||||
company_dependent=True,
|
||||
comodel_name="rma.operation",
|
||||
string="Default RMA Customer Operation",
|
||||
)
|
||||
rma_supplier_operation_id = fields.Many2one(
|
||||
company_dependent=True,
|
||||
comodel_name="rma.operation",
|
||||
string="Default RMA Supplier Operation",
|
||||
)
|
||||
rma_approval_policy = fields.Selection(
|
||||
related="categ_id.rma_approval_policy", readonly=True
|
||||
)
|
||||
30
rma/models/product_category.py
Normal file
30
rma/models/product_category.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright 2017 ForgeFlow
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ProductCategory(models.Model):
|
||||
_inherit = "product.category"
|
||||
|
||||
rma_approval_policy = fields.Selection(
|
||||
selection=[("one_step", "One step"), ("two_step", "Two steps")],
|
||||
string="RMA Approval Policy",
|
||||
required=True,
|
||||
default="one_step",
|
||||
help="Options: \n "
|
||||
"* One step: Always auto-approve RMAs that only contain "
|
||||
"products within categories with this policy.\n"
|
||||
"* Two steps: A RMA containing a product within a category with "
|
||||
"this policy will request the RMA manager approval.",
|
||||
)
|
||||
rma_customer_operation_id = fields.Many2one(
|
||||
company_dependent=True,
|
||||
comodel_name="rma.operation",
|
||||
string="Default RMA Customer Operation",
|
||||
)
|
||||
rma_supplier_operation_id = fields.Many2one(
|
||||
company_dependent=True,
|
||||
comodel_name="rma.operation",
|
||||
string="Default RMA Supplier Operation",
|
||||
)
|
||||
14
rma/models/res_company.py
Normal file
14
rma/models/res_company.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
group_rma_delivery_address = fields.Boolean(
|
||||
string="RMA addresses",
|
||||
help="Display 3 fields on rma: partner, invoice address, delivery address",
|
||||
)
|
||||
|
||||
group_rma_lines = fields.Boolean(
|
||||
string="Use RMA groups", help="Group RMA lines in one RMA group"
|
||||
)
|
||||
22
rma/models/res_config_settings.py
Normal file
22
rma/models/res_config_settings.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
group_rma_delivery_address = fields.Boolean(
|
||||
related="company_id.group_rma_delivery_address",
|
||||
implied_group="rma.group_rma_delivery_invoice_address",
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
group_rma_lines = fields.Boolean(
|
||||
related="company_id.group_rma_lines",
|
||||
readonly=False,
|
||||
implied_group="rma.group_rma_groups",
|
||||
)
|
||||
|
||||
module_rma_account = fields.Boolean(string="RMA invoicing")
|
||||
27
rma/models/res_partner.py
Normal file
27
rma/models/res_partner.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright 2017-22 ForgeFlow
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
def _compute_rma_line_count(self):
|
||||
for rec in self:
|
||||
rec.rma_line_count = len(rec.rma_line_ids)
|
||||
|
||||
rma_line_ids = fields.One2many(
|
||||
comodel_name="rma.order.line", string="RMAs", inverse_name="partner_id"
|
||||
)
|
||||
rma_line_count = fields.Integer(
|
||||
compute="_compute_rma_line_count", compute_sudo=True
|
||||
)
|
||||
|
||||
def action_open_partner_rma(self):
|
||||
action = self.env.ref("rma.action_rma_customer_lines")
|
||||
result = action.sudo().read()[0]
|
||||
result["context"] = {"search_default_partner_id": self.id}
|
||||
result["domain"] = []
|
||||
result["display_name"] = "Partner RMAs"
|
||||
return result
|
||||
96
rma/models/rma_operation.py
Normal file
96
rma/models/rma_operation.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# Copyright 2017-18 ForgeFlow
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class RmaOperation(models.Model):
|
||||
_name = "rma.operation"
|
||||
_description = "RMA Operation"
|
||||
|
||||
@api.model
|
||||
def _default_warehouse_id(self):
|
||||
company_id = self.env.user.company_id.id
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", company_id)], 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
|
||||
|
||||
@api.model
|
||||
def _default_routes(self):
|
||||
op_type = self.env.context.get("default_type")
|
||||
if op_type == "customer":
|
||||
return self.env.ref("rma.route_rma_customer")
|
||||
elif op_type == "supplier":
|
||||
return self.env.ref("rma.route_rma_supplier")
|
||||
|
||||
name = fields.Char("Description", required=True)
|
||||
code = fields.Char(required=True)
|
||||
active = fields.Boolean(default=True)
|
||||
receipt_policy = fields.Selection(
|
||||
[
|
||||
("no", "Not required"),
|
||||
("ordered", "Based on Ordered Quantities"),
|
||||
("delivered", "Based on Delivered Quantities"),
|
||||
],
|
||||
default="no",
|
||||
)
|
||||
delivery_policy = fields.Selection(
|
||||
[
|
||||
("no", "Not required"),
|
||||
("ordered", "Based on Ordered Quantities"),
|
||||
("received", "Based on Received Quantities"),
|
||||
],
|
||||
default="no",
|
||||
)
|
||||
in_route_id = fields.Many2one(
|
||||
comodel_name="stock.route",
|
||||
string="Inbound Route",
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
default=lambda self: self._default_routes(),
|
||||
)
|
||||
out_route_id = fields.Many2one(
|
||||
comodel_name="stock.route",
|
||||
string="Outbound Route",
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
default=lambda self: self._default_routes(),
|
||||
)
|
||||
customer_to_supplier = fields.Boolean(
|
||||
string="The customer will send to the supplier"
|
||||
)
|
||||
supplier_to_customer = fields.Boolean(
|
||||
string="The supplier will send to the customer"
|
||||
)
|
||||
in_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Inbound Warehouse",
|
||||
default=lambda self: self._default_warehouse_id(),
|
||||
)
|
||||
out_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Outbound Warehouse",
|
||||
default=lambda self: self._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,
|
||||
)
|
||||
rma_line_ids = fields.One2many(
|
||||
comodel_name="rma.order.line", inverse_name="operation_id", string="RMA lines"
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self.env.user.company_id,
|
||||
)
|
||||
334
rma/models/rma_order.py
Normal file
334
rma/models/rma_order.py
Normal file
@@ -0,0 +1,334 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class RmaOrder(models.Model):
|
||||
_name = "rma.order"
|
||||
_description = "RMA Group"
|
||||
_inherit = ["mail.thread"]
|
||||
_order = "id desc"
|
||||
|
||||
@api.model
|
||||
def _get_default_type(self):
|
||||
if "supplier" in self.env.context:
|
||||
return "supplier"
|
||||
return "customer"
|
||||
|
||||
def _compute_in_shipment_count(self):
|
||||
for rec in self:
|
||||
pickings = self.env["stock.picking"]
|
||||
for line in rec.rma_line_ids:
|
||||
pickings |= line._get_in_pickings()
|
||||
rec.in_shipment_count = len(pickings)
|
||||
|
||||
def _compute_out_shipment_count(self):
|
||||
for rec in self:
|
||||
pickings = self.env["stock.picking"]
|
||||
for line in rec.rma_line_ids:
|
||||
pickings |= line._get_out_pickings()
|
||||
rec.out_shipment_count = len(pickings)
|
||||
|
||||
def _compute_supplier_line_count(self):
|
||||
self.supplier_line_count = len(
|
||||
self.rma_line_ids.filtered(lambda r: r.supplier_rma_line_ids)
|
||||
)
|
||||
|
||||
def _compute_line_count(self):
|
||||
for rec in self:
|
||||
rec.line_count = len(rec._get_valid_lines())
|
||||
|
||||
@api.depends("rma_line_ids", "rma_line_ids.state")
|
||||
def _compute_state(self):
|
||||
for rec in self:
|
||||
state = "draft"
|
||||
if rec.rma_line_ids:
|
||||
states = set(rec.rma_line_ids.mapped("state"))
|
||||
if states == {"cancel"}:
|
||||
state = "cancel"
|
||||
elif "draft" in states:
|
||||
state = "draft"
|
||||
elif "to_approved" in states:
|
||||
state = "to_approved"
|
||||
elif "approved" in states:
|
||||
state = "approved"
|
||||
else:
|
||||
state = "done"
|
||||
rec.state = state
|
||||
|
||||
@api.model
|
||||
def _default_date_rma(self):
|
||||
return datetime.now()
|
||||
|
||||
@api.model
|
||||
def _default_warehouse_id(self):
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", self.env.company.id)], limit=1
|
||||
)
|
||||
return warehouse
|
||||
|
||||
name = fields.Char(string="Group Number", index=True, copy=False)
|
||||
type = fields.Selection(
|
||||
[("customer", "Customer"), ("supplier", "Supplier")],
|
||||
required=True,
|
||||
default=lambda self: self._get_default_type(),
|
||||
readonly=True,
|
||||
)
|
||||
reference = fields.Char(
|
||||
string="Partner Reference", help="The partner reference of this RMA order."
|
||||
)
|
||||
description = fields.Text()
|
||||
comment = fields.Text("Additional Information")
|
||||
date_rma = fields.Datetime(
|
||||
compute="_compute_date_rma",
|
||||
inverse="_inverse_date_rma",
|
||||
string="Order Date",
|
||||
index=True,
|
||||
default=lambda self: self._default_date_rma(),
|
||||
)
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner", string="Partner", required=True
|
||||
)
|
||||
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 Shipments"
|
||||
)
|
||||
out_shipment_count = fields.Integer(
|
||||
compute="_compute_out_shipment_count", string="# of Outgoing Shipments"
|
||||
)
|
||||
line_count = fields.Integer(compute="_compute_line_count", string="# of RMA lines")
|
||||
supplier_line_count = fields.Integer(
|
||||
compute="_compute_supplier_line_count", string="# of Supplier RMAs"
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
"res.company",
|
||||
string="Company",
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
assigned_to = fields.Many2one(
|
||||
comodel_name="res.users",
|
||||
tracking=True,
|
||||
default=lambda self: self.env.uid,
|
||||
)
|
||||
requested_by = fields.Many2one(
|
||||
comodel_name="res.users",
|
||||
tracking=True,
|
||||
default=lambda self: self.env.uid,
|
||||
)
|
||||
in_route_id = fields.Many2one(
|
||||
"stock.route",
|
||||
string="Inbound Route",
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
)
|
||||
out_route_id = fields.Many2one(
|
||||
"stock.route",
|
||||
string="Outbound Route",
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
)
|
||||
in_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Inbound Warehouse",
|
||||
required=False,
|
||||
default=_default_warehouse_id,
|
||||
)
|
||||
out_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Outbound Warehouse",
|
||||
required=False,
|
||||
default=_default_warehouse_id,
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
comodel_name="stock.location",
|
||||
string="Send To This Company Location",
|
||||
)
|
||||
customer_to_supplier = fields.Boolean("The customer will send to the supplier")
|
||||
supplier_to_customer = fields.Boolean("The supplier will send to the customer")
|
||||
supplier_address_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Supplier Address",
|
||||
help="Address of the supplier in case of Customer RMA operation " "dropship.",
|
||||
)
|
||||
customer_address_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Customer Address",
|
||||
help="Address of the customer in case of Supplier RMA operation " "dropship.",
|
||||
)
|
||||
state = fields.Selection(
|
||||
compute=_compute_state,
|
||||
selection=[
|
||||
("draft", "Draft"),
|
||||
("to_approve", "To Approve"),
|
||||
("approved", "Approved"),
|
||||
("done", "Done"),
|
||||
("cancel", "Cancel"),
|
||||
],
|
||||
default="draft",
|
||||
store=True,
|
||||
)
|
||||
operation_default_id = fields.Many2one(
|
||||
comodel_name="rma.operation",
|
||||
required=False,
|
||||
string="Default Operation Type",
|
||||
)
|
||||
|
||||
@api.onchange(
|
||||
"operation_default_id",
|
||||
)
|
||||
def _onchange_operation(self):
|
||||
if self.operation_default_id:
|
||||
self.in_warehouse_id = self.operation_default_id.in_warehouse_id
|
||||
self.out_warehouse_id = self.operation_default_id.out_warehouse_id
|
||||
self.location_id = (
|
||||
self.operation_default_id.location_id or self.in_warehouse_id.lot_rma_id
|
||||
)
|
||||
self.customer_to_supplier = self.operation_default_id.customer_to_supplier
|
||||
self.supplier_to_customer = self.operation_default_id.supplier_to_customer
|
||||
self.in_route_id = self.operation_default_id.in_route_id
|
||||
self.out_route_id = self.operation_default_id.out_route_id
|
||||
|
||||
@api.depends("rma_line_ids.date_rma")
|
||||
def _compute_date_rma(self):
|
||||
"""If all order line have same date set date_rma.
|
||||
If no lines, respect value given by the user.
|
||||
"""
|
||||
for rma in self:
|
||||
if rma.rma_line_ids:
|
||||
date_rma = rma.rma_line_ids[0].date_rma or False
|
||||
for rma_line in rma.rma_line_ids:
|
||||
if rma_line.date_rma != date_rma:
|
||||
date_rma = False
|
||||
break
|
||||
rma.date_rma = date_rma
|
||||
|
||||
def _inverse_date_rma(self):
|
||||
"""When set date_rma set date_rma on all order lines"""
|
||||
for po in self:
|
||||
if po.date_rma:
|
||||
po.rma_line_ids.write({"date_rma": po.date_rma})
|
||||
|
||||
@api.constrains("partner_id", "rma_line_ids")
|
||||
def _check_partner_id(self):
|
||||
if self.rma_line_ids and self.partner_id != self.mapped(
|
||||
"rma_line_ids.partner_id"
|
||||
):
|
||||
raise UserError(_("Group partner and RMA's partner must be the same."))
|
||||
if len(self.mapped("rma_line_ids.partner_id")) > 1:
|
||||
raise UserError(_("All grouped RMA's should have same partner."))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if self.env.context.get("supplier") or vals.get("type") == "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().create(vals_list)
|
||||
|
||||
def _view_shipments(self, result, shipments):
|
||||
# choose the view_mode accordingly
|
||||
if len(shipments) > 1:
|
||||
result["domain"] = [("id", "in", shipments.ids)]
|
||||
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.ids[0]
|
||||
return result
|
||||
|
||||
def action_view_in_shipments(self):
|
||||
action = self.env.ref("stock.action_picking_tree_all")
|
||||
result = action.sudo().read()[0]
|
||||
shipments = self.env["stock.picking"]
|
||||
for line in self.rma_line_ids:
|
||||
shipments |= line._get_in_pickings()
|
||||
return self._view_shipments(result, shipments)
|
||||
|
||||
def action_view_out_shipments(self):
|
||||
action = self.env.ref("stock.action_picking_tree_all")
|
||||
result = action.sudo().read()[0]
|
||||
shipments = self.env["stock.picking"]
|
||||
for line in self.rma_line_ids:
|
||||
shipments |= line._get_out_pickings()
|
||||
return self._view_shipments(result, shipments)
|
||||
|
||||
def _get_valid_lines(self):
|
||||
""":return: A recordset of rma lines."""
|
||||
self.ensure_one()
|
||||
return self.rma_line_ids
|
||||
|
||||
def action_view_lines(self):
|
||||
if self.type == "customer":
|
||||
action = self.env.ref("rma.action_rma_customer_lines")
|
||||
res = self.env.ref("rma.view_rma_line_form", False)
|
||||
else:
|
||||
action = self.env.ref("rma.action_rma_supplier_lines")
|
||||
res = self.env.ref("rma.view_rma_line_supplier_form", False)
|
||||
result = action.sudo().read()[0]
|
||||
lines = self._get_valid_lines()
|
||||
# choose the view_mode accordingly
|
||||
if len(lines.ids) != 1:
|
||||
result["domain"] = [("id", "in", lines.ids)]
|
||||
else:
|
||||
result["views"] = [(res and res.id or False, "form")]
|
||||
result["res_id"] = lines.id
|
||||
result["context"] = {}
|
||||
return result
|
||||
|
||||
def action_view_supplier_lines(self):
|
||||
action = self.env.ref("rma.action_rma_supplier_lines")
|
||||
result = action.sudo().read()[0]
|
||||
lines = self.rma_line_ids
|
||||
for line_id in lines:
|
||||
related_lines = [line.id for line in line_id.supplier_rma_line_ids]
|
||||
# choose the view_mode accordingly
|
||||
if len(related_lines) != 1:
|
||||
result["domain"] = [("id", "in", related_lines)]
|
||||
else:
|
||||
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
|
||||
|
||||
def action_rma_to_approve(self):
|
||||
return self.rma_line_ids.action_rma_to_approve()
|
||||
|
||||
def action_rma_draft(self):
|
||||
return self.rma_line_ids.action_rma_draft()
|
||||
|
||||
def action_rma_approve(self):
|
||||
return self.rma_line_ids.action_rma_approve()
|
||||
|
||||
def action_rma_done(self):
|
||||
return self.rma_line_ids.action_rma_done()
|
||||
|
||||
def action_rma_cancel(self):
|
||||
return self.rma_line_ids.action_rma_cancel()
|
||||
|
||||
@api.onchange("in_warehouse_id")
|
||||
def _onchange_in_warehouse_id(self):
|
||||
if self.in_warehouse_id and self.rma_line_ids:
|
||||
for rma_line in self.rma_line_ids:
|
||||
rma_line.in_warehouse_id = self.in_warehouse_id.id
|
||||
rma_line.location_id = self.in_warehouse_id.lot_rma_id.id
|
||||
|
||||
@api.onchange("customer_to_supplier", "supplier_address_id")
|
||||
def _onchange_customer_to_supplier(self):
|
||||
if self.type == "customer" and self.rma_line_ids:
|
||||
for rma_line in self.rma_line_ids:
|
||||
rma_line.customer_to_supplier = self.customer_to_supplier
|
||||
rma_line.supplier_address_id = self.supplier_address_id.id
|
||||
|
||||
@api.onchange("supplier_to_customer", "customer_address_id")
|
||||
def _onchange_supplier_to_customer(self):
|
||||
if self.type == "supplier" and self.rma_line_ids:
|
||||
for rma_line in self.rma_line_ids:
|
||||
rma_line.supplier_to_customer = self.supplier_to_customer
|
||||
rma_line.customer_address_id = self.customer_address_id.id
|
||||
807
rma/models/rma_order_line.py
Normal file
807
rma/models/rma_order_line.py
Normal file
@@ -0,0 +1,807 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
import operator
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
ops = {"=": operator.eq, "!=": operator.ne}
|
||||
|
||||
|
||||
class RmaOrderLine(models.Model):
|
||||
_name = "rma.order.line"
|
||||
_description = "RMA"
|
||||
_inherit = ["mail.thread"]
|
||||
_order = "id desc"
|
||||
|
||||
@api.model
|
||||
def _get_default_type(self):
|
||||
if "supplier" in self.env.context:
|
||||
return "supplier"
|
||||
return "customer"
|
||||
|
||||
@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.onchange("partner_id")
|
||||
def _onchange_delivery_address(self):
|
||||
self.delivery_address_id = self.env["res.partner"].browse(
|
||||
self.partner_id.address_get(["delivery"])["delivery"]
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _get_in_pickings(self):
|
||||
# We consider an in move one where the first origin is outside
|
||||
# of the company and the final destination is outside. In case
|
||||
# of 2 or 3 step pickings, we should categorize as in shipments
|
||||
# even when they are technically internal transfers.
|
||||
pickings = self.env["stock.picking"]
|
||||
for move in self.move_ids:
|
||||
first_usage = move._get_first_usage()
|
||||
last_usage = move._get_last_usage()
|
||||
if last_usage == "internal" and first_usage != "internal":
|
||||
pickings |= move.picking_id
|
||||
elif last_usage == "supplier" and first_usage == "customer":
|
||||
pickings |= move.picking_id
|
||||
return pickings
|
||||
|
||||
@api.model
|
||||
def _get_in_moves(self):
|
||||
moves = self.env["stock.move"]
|
||||
for move in self.move_ids:
|
||||
first_usage = move._get_first_usage()
|
||||
last_usage = move._get_last_usage()
|
||||
if last_usage == "internal" and first_usage != "internal":
|
||||
moves |= move
|
||||
elif last_usage == "supplier" and first_usage == "customer":
|
||||
moves |= move
|
||||
return moves
|
||||
|
||||
@api.model
|
||||
def _get_out_pickings(self):
|
||||
pickings = self.env["stock.picking"]
|
||||
for move in self.move_ids:
|
||||
first_usage = move._get_first_usage()
|
||||
last_usage = move._get_last_usage()
|
||||
if first_usage in ("internal", "production") and last_usage in (
|
||||
"customer",
|
||||
"supplier",
|
||||
):
|
||||
pickings |= move.picking_id
|
||||
elif last_usage == "customer" and first_usage == "supplier":
|
||||
pickings |= move.picking_id
|
||||
return pickings
|
||||
|
||||
def _compute_in_shipment_count(self):
|
||||
for line in self:
|
||||
pickings = line._get_in_pickings()
|
||||
line.in_shipment_count = len(pickings)
|
||||
|
||||
def _compute_out_shipment_count(self):
|
||||
for line in self:
|
||||
pickings = line._get_out_pickings()
|
||||
line.out_shipment_count = len(pickings)
|
||||
|
||||
def _get_rma_move_qty(self, states, direction="in"):
|
||||
for rec in self:
|
||||
product_obj = self.env["uom.uom"]
|
||||
qty = 0.0
|
||||
if direction == "in":
|
||||
moves = rec.move_ids.filtered(
|
||||
lambda m: m.state in states
|
||||
and (
|
||||
m.location_id.usage == "supplier"
|
||||
or m.location_id.usage == "customer"
|
||||
)
|
||||
and (
|
||||
m.location_dest_id.usage == "internal"
|
||||
or m.location_dest_id.usage == "supplier"
|
||||
)
|
||||
)
|
||||
elif direction == "out":
|
||||
moves = rec.move_ids.filtered(
|
||||
lambda m: m.state in states
|
||||
and (
|
||||
m.location_dest_id.usage == "supplier"
|
||||
or m.location_dest_id.usage == "customer"
|
||||
)
|
||||
and (
|
||||
m.location_id.usage == "internal"
|
||||
or m.location_id.usage == "supplier"
|
||||
)
|
||||
)
|
||||
for move in moves:
|
||||
# If the move is part of a chain don't count it
|
||||
if direction == "out" and move.move_orig_ids:
|
||||
continue
|
||||
elif direction == "in" and move.move_dest_ids:
|
||||
continue
|
||||
qty += product_obj._compute_quantity(move.product_uom_qty, rec.uom_id)
|
||||
return qty
|
||||
|
||||
@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 rec.receipt_policy == "delivered":
|
||||
rec.qty_to_receive = rec.qty_delivered - rec.qty_received
|
||||
|
||||
@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 rec.delivery_policy == "received":
|
||||
rec.qty_to_deliver = rec.qty_received - rec.qty_delivered
|
||||
|
||||
@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.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.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.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.depends(
|
||||
"customer_to_supplier",
|
||||
"supplier_rma_line_ids",
|
||||
"move_ids",
|
||||
"move_ids.state",
|
||||
"qty_received",
|
||||
"receipt_policy",
|
||||
"product_qty",
|
||||
"type",
|
||||
)
|
||||
def _compute_qty_supplier_rma(self):
|
||||
for rec in self:
|
||||
if rec.customer_to_supplier:
|
||||
supplier_rma_qty = rec._get_supplier_rma_qty()
|
||||
rec.qty_to_supplier_rma = rec.product_qty - supplier_rma_qty
|
||||
rec.qty_in_supplier_rma = supplier_rma_qty
|
||||
else:
|
||||
rec.qty_to_supplier_rma = 0.0
|
||||
rec.qty_in_supplier_rma = 0.0
|
||||
|
||||
def _compute_rma_line_count(self):
|
||||
for rec in self.filtered(lambda r: r.type == "customer"):
|
||||
rec.rma_line_count = len(rec.supplier_rma_line_ids)
|
||||
for rec in self.filtered(lambda r: r.type == "supplier"):
|
||||
rec.rma_line_count = len(rec.customer_rma_id)
|
||||
|
||||
@api.model
|
||||
def _default_date_rma(self):
|
||||
return fields.Datetime.now()
|
||||
|
||||
delivery_address_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Partner delivery address",
|
||||
help="This address will be used to deliver repaired or replacement "
|
||||
"products.",
|
||||
)
|
||||
rma_id = fields.Many2one(
|
||||
comodel_name="rma.order",
|
||||
string="RMA Group",
|
||||
tracking=True,
|
||||
readonly=True,
|
||||
)
|
||||
name = fields.Char(
|
||||
string="Reference",
|
||||
required=True,
|
||||
default="/",
|
||||
help="Add here the supplier RMA #. Otherwise an internal code is" " assigned.",
|
||||
copy=False,
|
||||
)
|
||||
description = fields.Text()
|
||||
conditions = fields.Html(string="Terms and conditions")
|
||||
origin = fields.Char(
|
||||
string="Source Document",
|
||||
help="Reference of the document that produced this rma.",
|
||||
)
|
||||
date_rma = fields.Datetime(
|
||||
string="Order Date", index=True, default=lambda self: self._default_date_rma()
|
||||
)
|
||||
state = fields.Selection(
|
||||
selection=[
|
||||
("draft", "Draft"),
|
||||
("to_approve", "To Approve"),
|
||||
("approved", "Approved"),
|
||||
("done", "Done"),
|
||||
("canceled", "Canceled"),
|
||||
],
|
||||
default="draft",
|
||||
tracking=True,
|
||||
)
|
||||
operation_id = fields.Many2one(
|
||||
comodel_name="rma.operation",
|
||||
required=True,
|
||||
readonly=False,
|
||||
tracking=True,
|
||||
)
|
||||
assigned_to = fields.Many2one(
|
||||
comodel_name="res.users",
|
||||
tracking=True,
|
||||
default=lambda self: self.env.uid,
|
||||
)
|
||||
requested_by = fields.Many2one(
|
||||
comodel_name="res.users",
|
||||
tracking=True,
|
||||
default=lambda self: self.env.uid,
|
||||
)
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
required=True,
|
||||
store=True,
|
||||
tracking=True,
|
||||
)
|
||||
sequence = fields.Integer(
|
||||
default=10, help="Gives the sequence of this line when displaying the rma."
|
||||
)
|
||||
product_id = fields.Many2one(
|
||||
comodel_name="product.product",
|
||||
ondelete="restrict",
|
||||
required=True,
|
||||
)
|
||||
product_tracking = fields.Selection(related="product_id.tracking")
|
||||
lot_id = fields.Many2one(
|
||||
comodel_name="stock.lot",
|
||||
string="Lot/Serial Number",
|
||||
)
|
||||
product_qty = fields.Float(
|
||||
string="Return Qty",
|
||||
copy=False,
|
||||
default=1.0,
|
||||
digits="Product Unit of Measure",
|
||||
)
|
||||
uom_id = fields.Many2one(
|
||||
comodel_name="uom.uom",
|
||||
string="Unit of Measure",
|
||||
required=True,
|
||||
compute="_compute_uom_id",
|
||||
precompute=True,
|
||||
store=True,
|
||||
)
|
||||
price_unit = fields.Monetary(
|
||||
string="Unit cost",
|
||||
help="Unit cost of the items under RMA",
|
||||
)
|
||||
in_shipment_count = fields.Integer(
|
||||
compute="_compute_in_shipment_count", string="# of Shipments"
|
||||
)
|
||||
out_shipment_count = fields.Integer(
|
||||
compute="_compute_out_shipment_count", string="# of Deliveries"
|
||||
)
|
||||
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",
|
||||
copy=False,
|
||||
)
|
||||
currency_id = fields.Many2one(
|
||||
"res.currency",
|
||||
default=lambda self: self.env.company.currency_id,
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
required=True,
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
type = fields.Selection(
|
||||
selection=[("customer", "Customer"), ("supplier", "Supplier")],
|
||||
required=True,
|
||||
default=lambda self: self._get_default_type(),
|
||||
readonly=True,
|
||||
)
|
||||
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,
|
||||
default="no",
|
||||
readonly=False,
|
||||
)
|
||||
delivery_policy = fields.Selection(
|
||||
[
|
||||
("no", "Not required"),
|
||||
("ordered", "Based on Ordered Quantities"),
|
||||
("received", "Based on Received Quantities"),
|
||||
],
|
||||
required=True,
|
||||
default="no",
|
||||
readonly=False,
|
||||
ondelete="cascade",
|
||||
)
|
||||
in_route_id = fields.Many2one(
|
||||
"stock.route",
|
||||
string="Inbound Route",
|
||||
required=True,
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
compute="_compute_in_route_id",
|
||||
precompute=True,
|
||||
store=True,
|
||||
)
|
||||
out_route_id = fields.Many2one(
|
||||
"stock.route",
|
||||
string="Outbound Route",
|
||||
required=True,
|
||||
domain=[("rma_selectable", "=", True)],
|
||||
compute="_compute_out_route_id",
|
||||
precompute=True,
|
||||
store=True,
|
||||
)
|
||||
in_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Inbound Warehouse",
|
||||
required=True,
|
||||
default=lambda self: self._default_warehouse_id(),
|
||||
)
|
||||
out_warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
string="Outbound Warehouse",
|
||||
required=True,
|
||||
default=lambda self: self._default_warehouse_id(),
|
||||
)
|
||||
location_id = fields.Many2one(
|
||||
comodel_name="stock.location",
|
||||
string="Send To This Company Location",
|
||||
required=True,
|
||||
default=lambda self: self._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")
|
||||
rma_line_count = fields.Integer(
|
||||
compute="_compute_rma_line_count", string="# of RMA lines associated"
|
||||
)
|
||||
supplier_address_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Supplier Address",
|
||||
help="Address of the supplier in case of Customer RMA operation " "dropship.",
|
||||
)
|
||||
customer_address_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Customer Address",
|
||||
help="Address of the customer in case of Supplier RMA operation " "dropship.",
|
||||
)
|
||||
qty_to_receive = fields.Float(
|
||||
digits="Product Unit of Measure",
|
||||
compute="_compute_qty_to_receive",
|
||||
store=True,
|
||||
)
|
||||
qty_incoming = fields.Float(
|
||||
string="Incoming Qty",
|
||||
copy=False,
|
||||
readonly=True,
|
||||
digits="Product Unit of Measure",
|
||||
compute="_compute_qty_incoming",
|
||||
store=True,
|
||||
)
|
||||
qty_received = fields.Float(
|
||||
copy=False,
|
||||
digits="Product Unit of Measure",
|
||||
compute="_compute_qty_received",
|
||||
store=True,
|
||||
)
|
||||
qty_to_deliver = fields.Float(
|
||||
copy=False,
|
||||
digits="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="Product Unit of Measure",
|
||||
compute="_compute_qty_outgoing",
|
||||
store=True,
|
||||
)
|
||||
qty_delivered = fields.Float(
|
||||
copy=False,
|
||||
digits="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="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="Product Unit of Measure",
|
||||
readonly=True,
|
||||
compute="_compute_qty_supplier_rma",
|
||||
store=True,
|
||||
)
|
||||
under_warranty = fields.Boolean(string="Under Warranty?")
|
||||
|
||||
def _prepare_rma_line_from_stock_move(self, sm, lot=False):
|
||||
if not self.type:
|
||||
self.type = self._get_default_type()
|
||||
if self.type == "customer":
|
||||
operation = (
|
||||
sm.product_id.rma_customer_operation_id
|
||||
or sm.product_id.categ_id.rma_customer_operation_id
|
||||
)
|
||||
else:
|
||||
operation = (
|
||||
sm.product_id.rma_supplier_operation_id
|
||||
or sm.product_id.categ_id.rma_supplier_operation_id
|
||||
)
|
||||
|
||||
if not operation:
|
||||
operation = self.env["rma.operation"].search(
|
||||
[("type", "=", self.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.route"].search(
|
||||
[("rma_selectable", "=", True)], limit=1
|
||||
)
|
||||
if not route:
|
||||
raise ValidationError(_("Please define an RMA route."))
|
||||
|
||||
if (
|
||||
not operation.in_warehouse_id
|
||||
or not operation.out_warehouse_id
|
||||
or not (
|
||||
operation.in_warehouse_id.lot_rma_id
|
||||
or operation.out_warehouse_id.lot_rma_id
|
||||
)
|
||||
):
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", self.company_id.id), ("lot_rma_id", "!=", False)],
|
||||
limit=1,
|
||||
)
|
||||
if not warehouse:
|
||||
raise ValidationError(
|
||||
_("Please define a warehouse with a default RMA location.")
|
||||
)
|
||||
|
||||
data = {
|
||||
"product_id": sm.product_id.id,
|
||||
"lot_id": lot and lot.id or False,
|
||||
"origin": sm.picking_id.name or sm.name,
|
||||
"uom_id": sm.product_uom.id,
|
||||
"product_qty": sm.product_uom_qty,
|
||||
"delivery_address_id": sm.picking_id.partner_id.id,
|
||||
"operation_id": operation.id,
|
||||
"receipt_policy": operation.receipt_policy,
|
||||
"delivery_policy": operation.delivery_policy,
|
||||
"in_warehouse_id": operation.in_warehouse_id.id or warehouse.id,
|
||||
"out_warehouse_id": operation.out_warehouse_id.id or warehouse.id,
|
||||
"in_route_id": operation.in_route_id.id or route.id,
|
||||
"out_route_id": operation.out_route_id.id or route.id,
|
||||
"location_id": (
|
||||
operation.location_id.id
|
||||
or operation.in_warehouse_id.lot_rma_id.id
|
||||
or operation.out_warehouse_id.lot_rma_id.id
|
||||
or warehouse.lot_rma_id.id
|
||||
),
|
||||
}
|
||||
return data
|
||||
|
||||
@api.onchange("reference_move_id")
|
||||
def _onchange_reference_move_id(self):
|
||||
self.ensure_one()
|
||||
sm = self.reference_move_id
|
||||
if not sm:
|
||||
return
|
||||
if sm.move_line_ids.lot_id:
|
||||
if len(sm.move_line_ids.lot_id) > 1:
|
||||
raise UserError(_("To manage lots use RMA groups."))
|
||||
else:
|
||||
data = self._prepare_rma_line_from_stock_move(
|
||||
sm, lot=sm.move_line_ids.lot_id[0]
|
||||
)
|
||||
self.update(data)
|
||||
else:
|
||||
data = self._prepare_rma_line_from_stock_move(sm, lot=False)
|
||||
self.update(data)
|
||||
self._remove_other_data_origin("reference_move_id")
|
||||
|
||||
@api.constrains("reference_move_id", "partner_id")
|
||||
def _check_move_partner(self):
|
||||
for rec in self:
|
||||
if (
|
||||
rec.reference_move_id
|
||||
and rec.reference_move_id.picking_id.partner_id != rec.partner_id
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"RMA customer and originating stock move customer "
|
||||
"doesn't match."
|
||||
)
|
||||
)
|
||||
|
||||
def _remove_other_data_origin(self, exception):
|
||||
if not exception == "reference_move_id":
|
||||
self.reference_move_id = False
|
||||
return True
|
||||
|
||||
def _check_lot_assigned(self):
|
||||
for rec in self:
|
||||
if rec.product_id.tracking == "serial" and rec.product_qty != 1:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Product %s has serial tracking configuration, "
|
||||
"quantity to receive should be 1"
|
||||
)
|
||||
% (rec.product_id.display_name)
|
||||
)
|
||||
|
||||
def action_rma_to_approve(self):
|
||||
self._check_lot_assigned()
|
||||
self.write({"state": "to_approve"})
|
||||
for rec in self:
|
||||
if rec.product_id.rma_approval_policy == "one_step":
|
||||
rec.action_rma_approve()
|
||||
return True
|
||||
|
||||
def action_rma_draft(self):
|
||||
self.write({"state": "draft"})
|
||||
return True
|
||||
|
||||
def action_rma_approve(self):
|
||||
self.write({"state": "approved"})
|
||||
return True
|
||||
|
||||
def action_rma_done(self):
|
||||
self.write({"state": "done"})
|
||||
return True
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if not vals.get("name") or vals.get("name") == "/":
|
||||
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().create(vals_list)
|
||||
|
||||
def check_cancel(self):
|
||||
for move in self.move_ids:
|
||||
if move.state == "done":
|
||||
raise UserError(
|
||||
_("Unable to cancel %s as some receptions have already been done.")
|
||||
% (self.name)
|
||||
)
|
||||
|
||||
def action_rma_cancel(self):
|
||||
for order in self:
|
||||
order.check_cancel()
|
||||
order.write({"state": "canceled"})
|
||||
order.move_ids._action_cancel()
|
||||
shipments = order._get_in_pickings()
|
||||
shipments |= order._get_out_pickings()
|
||||
for ship in shipments:
|
||||
ship.action_cancel()
|
||||
return True
|
||||
|
||||
def _get_price_unit(self):
|
||||
"""The price unit corresponds to the cost of that product"""
|
||||
self.ensure_one()
|
||||
if self.reference_move_id:
|
||||
price_unit = self.reference_move_id.price_unit
|
||||
else:
|
||||
price_unit = self.product_id.with_company(self.company_id).standard_price
|
||||
return price_unit
|
||||
|
||||
@api.onchange("product_id")
|
||||
def _onchange_product_id(self):
|
||||
result = {}
|
||||
if not self.product_id:
|
||||
return result
|
||||
self.uom_id = self.product_id.uom_id.id
|
||||
if not self.type:
|
||||
self.type = self._get_default_type()
|
||||
self.price_unit = self._get_price_unit()
|
||||
if self.type == "customer":
|
||||
self.operation_id = (
|
||||
self.product_id.rma_customer_operation_id
|
||||
or self.product_id.categ_id.rma_customer_operation_id
|
||||
)
|
||||
else:
|
||||
self.operation_id = (
|
||||
self.product_id.rma_supplier_operation_id
|
||||
or self.product_id.categ_id.rma_supplier_operation_id
|
||||
)
|
||||
if self.lot_id.product_id != self.product_id:
|
||||
self.lot_id = False
|
||||
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.customer_to_supplier = (
|
||||
self.rma_id.customer_to_supplier or self.operation_id.customer_to_supplier
|
||||
)
|
||||
self.supplier_to_customer = (
|
||||
self.rma_id.supplier_to_customer or self.operation_id.supplier_to_customer
|
||||
)
|
||||
self.in_warehouse_id = (
|
||||
self.rma_id.in_warehouse_id or self.operation_id.in_warehouse_id
|
||||
)
|
||||
self.out_warehouse_id = (
|
||||
self.rma_id.out_warehouse_id or self.operation_id.out_warehouse_id
|
||||
)
|
||||
self.location_id = (
|
||||
self.rma_id.location_id
|
||||
or self.operation_id.location_id
|
||||
or self.in_warehouse_id.lot_rma_id
|
||||
)
|
||||
self.in_route_id = self.rma_id.in_route_id or self.operation_id.in_route_id
|
||||
self.out_route_id = self.rma_id.out_route_id or self.operation_id.out_route_id
|
||||
return result
|
||||
|
||||
@api.depends("operation_id")
|
||||
def _compute_in_route_id(self):
|
||||
for rec in self:
|
||||
rec.in_route_id = rec.operation_id.in_route_id
|
||||
|
||||
@api.depends("operation_id")
|
||||
def _compute_out_route_id(self):
|
||||
for rec in self:
|
||||
rec.out_route_id = rec.operation_id.out_route_id
|
||||
|
||||
@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("lot_id")
|
||||
def _onchange_lot_id(self):
|
||||
product = self.lot_id.product_id
|
||||
if product:
|
||||
self.product_id = product
|
||||
|
||||
@api.depends("product_id")
|
||||
def _compute_uom_id(self):
|
||||
for rec in self:
|
||||
if rec.product_id:
|
||||
rec.uom_id = rec.product_id.uom_id
|
||||
|
||||
def action_view_in_shipments(self):
|
||||
action = self.env.ref("stock.action_picking_tree_all")
|
||||
result = action.sudo().read()[0]
|
||||
shipments = self.env["stock.picking"]
|
||||
for line in self:
|
||||
shipments |= line._get_in_pickings()
|
||||
# choose the view_mode accordingly
|
||||
if len(shipments) != 1:
|
||||
result["domain"] = "[('id', 'in', " + str(shipments.ids) + ")]"
|
||||
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.ids[0]
|
||||
return result
|
||||
|
||||
def action_view_out_shipments(self):
|
||||
action = self.env.ref("stock.action_picking_tree_all")
|
||||
result = action.sudo().read()[0]
|
||||
shipments = self.env["stock.picking"]
|
||||
for line in self:
|
||||
shipments |= line._get_out_pickings()
|
||||
# choose the view_mode accordingly
|
||||
if len(shipments) != 1:
|
||||
result["domain"] = "[('id', 'in', " + str(shipments.ids) + ")]"
|
||||
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.ids[0]
|
||||
return result
|
||||
|
||||
def action_view_rma_lines(self):
|
||||
if self.type == "customer":
|
||||
# from customer we link to supplier rma
|
||||
action = self.env.ref("rma.action_rma_supplier_lines")
|
||||
rma_lines = self.supplier_rma_line_ids.ids
|
||||
res = self.env.ref("rma.view_rma_line_supplier_form", False)
|
||||
else:
|
||||
# from supplier we link to customer rma
|
||||
action = self.env.ref("rma.action_rma_customer_lines")
|
||||
rma_lines = self.customer_rma_id.ids
|
||||
res = self.env.ref("rma.view_rma_line_form", False)
|
||||
result = action.sudo().read()[0]
|
||||
# choose the view_mode accordingly
|
||||
if rma_lines and len(rma_lines) != 1:
|
||||
result["domain"] = rma_lines.ids
|
||||
elif len(rma_lines) == 1:
|
||||
result["views"] = [(res and res.id or False, "form")]
|
||||
result["res_id"] = rma_lines[0]
|
||||
return result
|
||||
|
||||
@api.constrains("partner_id", "rma_id")
|
||||
def _check_partner_id(self):
|
||||
if self.rma_id and self.partner_id != self.rma_id.partner_id:
|
||||
raise ValidationError(
|
||||
_("Group partner and RMA's partner must be the same.")
|
||||
)
|
||||
107
rma/models/stock_move.py
Normal file
107
rma/models/stock_move.py
Normal file
@@ -0,0 +1,107 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
rma_line_id = fields.Many2one(
|
||||
"rma.order.line", string="RMA line", ondelete="restrict"
|
||||
)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals.get("group_id"):
|
||||
group = self.env["procurement.group"].browse(vals["group_id"])
|
||||
if group.rma_line_id:
|
||||
vals["rma_line_id"] = group.rma_line_id.id
|
||||
return super().create(vals_list)
|
||||
|
||||
def _action_assign(self, force_qty=False):
|
||||
res = super()._action_assign(force_qty=force_qty)
|
||||
for move in self:
|
||||
if move.rma_line_id:
|
||||
move.partner_id = move.rma_line_id.partner_id.id or False
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _get_first_usage(self):
|
||||
if self.move_orig_ids:
|
||||
# We assume here that all origin moves come from the same place
|
||||
return self.move_orig_ids[0]._get_first_usage()
|
||||
else:
|
||||
return self.location_id.usage
|
||||
|
||||
@api.model
|
||||
def _get_last_usage(self):
|
||||
if self.move_dest_ids:
|
||||
# We assume here that all origin moves come from the same place
|
||||
return self.move_dest_ids[0]._get_last_usage()
|
||||
else:
|
||||
return self.location_dest_id.usage
|
||||
|
||||
def _should_bypass_reservation(self, forced_location=False):
|
||||
res = super()._should_bypass_reservation(forced_location=forced_location)
|
||||
if self.env.context.get("force_no_bypass_reservation"):
|
||||
return False
|
||||
return res
|
||||
|
||||
def _get_available_quantity(
|
||||
self,
|
||||
location_id,
|
||||
lot_id=None,
|
||||
package_id=None,
|
||||
owner_id=None,
|
||||
strict=False,
|
||||
allow_negative=False,
|
||||
):
|
||||
if (
|
||||
not lot_id
|
||||
and self.rma_line_id.lot_id
|
||||
and self.location_id.usage == "internal"
|
||||
):
|
||||
# In supplier RMA deliveries we can only send the RMA lot/serial.
|
||||
lot_id = self.rma_line_id.lot_id
|
||||
return super()._get_available_quantity(
|
||||
location_id,
|
||||
lot_id=lot_id,
|
||||
package_id=package_id,
|
||||
owner_id=owner_id,
|
||||
strict=strict,
|
||||
allow_negative=allow_negative,
|
||||
)
|
||||
|
||||
def _update_reserved_quantity(
|
||||
self,
|
||||
need,
|
||||
location_id,
|
||||
quant_ids=None,
|
||||
lot_id=None,
|
||||
package_id=None,
|
||||
owner_id=None,
|
||||
strict=True,
|
||||
):
|
||||
if (
|
||||
not lot_id
|
||||
and self.rma_line_id.lot_id
|
||||
and self.location_id.usage == "internal"
|
||||
):
|
||||
# In supplier RMA deliveries we can only send the RMA lot/serial.
|
||||
lot_id = self.rma_line_id.lot_id
|
||||
return super()._update_reserved_quantity(
|
||||
need,
|
||||
location_id,
|
||||
quant_ids=quant_ids,
|
||||
lot_id=lot_id,
|
||||
package_id=package_id,
|
||||
owner_id=owner_id,
|
||||
strict=strict,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _prepare_merge_moves_distinct_fields(self):
|
||||
res = super()._prepare_merge_moves_distinct_fields()
|
||||
return res + ["rma_line_id"]
|
||||
14
rma/models/stock_quant_package.py
Normal file
14
rma/models/stock_quant_package.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2023 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class QuantPackage(models.Model):
|
||||
_inherit = "stock.quant.package"
|
||||
|
||||
def _allowed_to_move_between_transfers(self):
|
||||
res = super()._allowed_to_move_between_transfers()
|
||||
return res and self.location_id != self.env.ref(
|
||||
"stock.stock_location_customers"
|
||||
)
|
||||
40
rma/models/stock_rule.py
Normal file
40
rma/models/stock_rule.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2017-22 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class StockRule(models.Model):
|
||||
_inherit = "stock.rule"
|
||||
|
||||
def _get_stock_move_values(
|
||||
self,
|
||||
product_id,
|
||||
product_qty,
|
||||
product_uom,
|
||||
location_id,
|
||||
name,
|
||||
origin,
|
||||
company_id,
|
||||
values,
|
||||
):
|
||||
res = super()._get_stock_move_values(
|
||||
product_id,
|
||||
product_qty,
|
||||
product_uom,
|
||||
location_id,
|
||||
name,
|
||||
origin,
|
||||
company_id,
|
||||
values,
|
||||
)
|
||||
if "rma_line_id" in values:
|
||||
line = values.get("rma_line_id")
|
||||
line = self.env["rma.order.line"].browse([line])
|
||||
res["rma_line_id"] = line.id
|
||||
if line.delivery_address_id:
|
||||
res["partner_id"] = line.delivery_address_id.id
|
||||
elif line.rma_id.partner_id:
|
||||
res["partner_id"] = line.rma_id.partner_id.id
|
||||
res["price_unit"] = line._get_price_unit()
|
||||
return res
|
||||
270
rma/models/stock_warehouse.py
Normal file
270
rma/models/stock_warehouse.py
Normal file
@@ -0,0 +1,270 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
||||
|
||||
class StockWarehouse(models.Model):
|
||||
_inherit = "stock.warehouse"
|
||||
|
||||
lot_rma_id = fields.Many2one(
|
||||
comodel_name="stock.location", string="RMA Location"
|
||||
) # not readonly to have the possibility to edit location and
|
||||
# propagate to rma rules (add a auto-update when writing this field?)
|
||||
rma_cust_out_type_id = fields.Many2one(
|
||||
comodel_name="stock.picking.type", string="RMA Customer out Type", readonly=True
|
||||
)
|
||||
rma_sup_out_type_id = fields.Many2one(
|
||||
comodel_name="stock.picking.type", string="RMA Supplier out Type", readonly=True
|
||||
)
|
||||
rma_cust_in_type_id = fields.Many2one(
|
||||
comodel_name="stock.picking.type", string="RMA Customer in Type", readonly=True
|
||||
)
|
||||
rma_sup_in_type_id = fields.Many2one(
|
||||
comodel_name="stock.picking.type", string="RMA Supplier in Type", readonly=True
|
||||
)
|
||||
rma_in_this_wh = fields.Boolean(
|
||||
string="RMA in this Warehouse",
|
||||
help="If set, it will create RMA location, picking types and routes "
|
||||
"for this warehouse.",
|
||||
)
|
||||
rma_customer_in_pull_id = fields.Many2one(
|
||||
comodel_name="stock.rule", string="RMA Customer In Rule"
|
||||
)
|
||||
rma_customer_out_pull_id = fields.Many2one(
|
||||
comodel_name="stock.rule", string="RMA Customer Out Rule"
|
||||
)
|
||||
rma_supplier_in_pull_id = fields.Many2one(
|
||||
comodel_name="stock.rule", string="RMA Supplier In Rule"
|
||||
)
|
||||
rma_supplier_out_pull_id = fields.Many2one(
|
||||
comodel_name="stock.rule", string="RMA Supplier Out Rule"
|
||||
)
|
||||
|
||||
def _get_rma_types(self):
|
||||
return [
|
||||
self.rma_cust_out_type_id,
|
||||
self.rma_sup_out_type_id,
|
||||
self.rma_cust_in_type_id,
|
||||
self.rma_sup_in_type_id,
|
||||
]
|
||||
|
||||
def _rma_types_available(self):
|
||||
self.ensure_one()
|
||||
rma_types = self._get_rma_types()
|
||||
for r_type in rma_types:
|
||||
if not r_type:
|
||||
return False
|
||||
return True
|
||||
|
||||
def write(self, vals):
|
||||
if "rma_in_this_wh" in vals:
|
||||
if vals.get("rma_in_this_wh"):
|
||||
for wh in self:
|
||||
# RMA location:
|
||||
if not wh.lot_rma_id:
|
||||
wh.lot_rma_id = self.env["stock.location"].create(
|
||||
{
|
||||
"name": "RMA",
|
||||
"usage": "internal",
|
||||
"location_id": wh.view_location_id.id,
|
||||
"company_id": wh.company_id.id,
|
||||
"return_location": True,
|
||||
}
|
||||
)
|
||||
# RMA types
|
||||
if not wh._rma_types_available():
|
||||
wh._create_rma_picking_types()
|
||||
else:
|
||||
for r_type in wh._get_rma_types():
|
||||
if r_type:
|
||||
r_type.active = True
|
||||
# RMA rules:
|
||||
wh._create_or_update_rma_pull()
|
||||
else:
|
||||
for wh in self:
|
||||
for r_type in wh._get_rma_types():
|
||||
if r_type:
|
||||
r_type.active = False
|
||||
# Unlink rules:
|
||||
self.mapped("rma_customer_in_pull_id").unlink()
|
||||
self.mapped("rma_customer_out_pull_id").unlink()
|
||||
self.mapped("rma_supplier_in_pull_id").unlink()
|
||||
self.mapped("rma_supplier_out_pull_id").unlink()
|
||||
return super().write(vals)
|
||||
|
||||
def _create_rma_picking_types(self):
|
||||
picking_type_obj = self.env["stock.picking.type"]
|
||||
customer_loc, supplier_loc = self._get_partner_locations()
|
||||
for wh in self:
|
||||
other_pick_type = picking_type_obj.search(
|
||||
[("warehouse_id", "=", wh.id)], order="sequence desc", limit=1
|
||||
)
|
||||
color = other_pick_type.color if other_pick_type else 0
|
||||
max_sequence = other_pick_type and other_pick_type.sequence or 0
|
||||
# create rma_cust_out_type_id:
|
||||
rma_cust_out_type_id = picking_type_obj.create(
|
||||
{
|
||||
"name": _("Customer RMA Deliveries"),
|
||||
"warehouse_id": wh.id,
|
||||
"code": "outgoing",
|
||||
"use_create_lots": True,
|
||||
"use_existing_lots": False,
|
||||
"sequence_id": self.env.ref("rma.seq_picking_type_rma_cust_out").id,
|
||||
"default_location_src_id": wh.lot_rma_id.id,
|
||||
"default_location_dest_id": customer_loc.id,
|
||||
"sequence": max_sequence,
|
||||
"color": color,
|
||||
"sequence_code": "RMA → Customer",
|
||||
}
|
||||
)
|
||||
# create rma_sup_out_type_id:
|
||||
rma_sup_out_type_id = picking_type_obj.create(
|
||||
{
|
||||
"name": _("Supplier RMA Deliveries"),
|
||||
"warehouse_id": wh.id,
|
||||
"code": "outgoing",
|
||||
"use_create_lots": True,
|
||||
"use_existing_lots": False,
|
||||
"sequence_id": self.env.ref("rma.seq_picking_type_rma_sup_out").id,
|
||||
"default_location_src_id": wh.lot_rma_id.id,
|
||||
"default_location_dest_id": supplier_loc.id,
|
||||
"sequence": max_sequence,
|
||||
"color": color,
|
||||
"sequence_code": "Customer → RMA",
|
||||
}
|
||||
)
|
||||
# create rma_cust_in_type_id:
|
||||
rma_cust_in_type_id = picking_type_obj.create(
|
||||
{
|
||||
"name": _("Customer RMA Receipts"),
|
||||
"warehouse_id": wh.id,
|
||||
"code": "incoming",
|
||||
"use_create_lots": True,
|
||||
"use_existing_lots": False,
|
||||
"sequence_id": self.env.ref("rma.seq_picking_type_rma_cust_in").id,
|
||||
"default_location_src_id": customer_loc.id,
|
||||
"default_location_dest_id": wh.lot_rma_id.id,
|
||||
"sequence": max_sequence,
|
||||
"color": color,
|
||||
"sequence_code": "RMA -> Supplier",
|
||||
}
|
||||
)
|
||||
# create rma_sup_in_type_id:
|
||||
rma_sup_in_type_id = picking_type_obj.create(
|
||||
{
|
||||
"name": _("Supplier RMA Receipts"),
|
||||
"warehouse_id": wh.id,
|
||||
"code": "incoming",
|
||||
"use_create_lots": True,
|
||||
"use_existing_lots": False,
|
||||
"sequence_id": self.env.ref("rma.seq_picking_type_rma_sup_in").id,
|
||||
"default_location_src_id": supplier_loc.id,
|
||||
"default_location_dest_id": wh.lot_rma_id.id,
|
||||
"sequence": max_sequence,
|
||||
"color": color,
|
||||
"sequence_code": "Supplier -> RMA",
|
||||
}
|
||||
)
|
||||
wh.write(
|
||||
{
|
||||
"rma_cust_out_type_id": rma_cust_out_type_id.id,
|
||||
"rma_sup_out_type_id": rma_sup_out_type_id.id,
|
||||
"rma_cust_in_type_id": rma_cust_in_type_id.id,
|
||||
"rma_sup_in_type_id": rma_sup_in_type_id.id,
|
||||
}
|
||||
)
|
||||
return True
|
||||
|
||||
def get_rma_rules_dict(self):
|
||||
self.ensure_one()
|
||||
rma_rules = dict()
|
||||
customer_loc, supplier_loc = self._get_partner_locations()
|
||||
rma_rules["rma_customer_in"] = {
|
||||
"name": self._format_rulename(self, customer_loc, self.lot_rma_id.name),
|
||||
"action": "pull",
|
||||
"warehouse_id": self.id,
|
||||
"company_id": self.company_id.id,
|
||||
"location_src_id": customer_loc.id,
|
||||
"location_dest_id": self.lot_rma_id.id,
|
||||
"procure_method": "make_to_stock",
|
||||
"route_id": self.env.ref("rma.route_rma_customer").id,
|
||||
"picking_type_id": self.rma_cust_in_type_id.id,
|
||||
"active": True,
|
||||
}
|
||||
rma_rules["rma_customer_out"] = {
|
||||
"name": self._format_rulename(self, self.lot_rma_id, customer_loc.name),
|
||||
"action": "pull",
|
||||
"warehouse_id": self.id,
|
||||
"company_id": self.company_id.id,
|
||||
"location_src_id": self.lot_rma_id.id,
|
||||
"location_dest_id": customer_loc.id,
|
||||
"procure_method": "make_to_stock",
|
||||
"route_id": self.env.ref("rma.route_rma_customer").id,
|
||||
"picking_type_id": self.rma_cust_out_type_id.id,
|
||||
"active": True,
|
||||
}
|
||||
rma_rules["rma_supplier_in"] = {
|
||||
"name": self._format_rulename(self, supplier_loc, self.lot_rma_id.name),
|
||||
"action": "pull",
|
||||
"warehouse_id": self.id,
|
||||
"company_id": self.company_id.id,
|
||||
"location_src_id": supplier_loc.id,
|
||||
"location_dest_id": self.lot_rma_id.id,
|
||||
"procure_method": "make_to_stock",
|
||||
"route_id": self.env.ref("rma.route_rma_supplier").id,
|
||||
"picking_type_id": self.rma_sup_in_type_id.id,
|
||||
"active": True,
|
||||
}
|
||||
rma_rules["rma_supplier_out"] = {
|
||||
"name": self._format_rulename(self, self.lot_rma_id, supplier_loc.name),
|
||||
"action": "pull",
|
||||
"warehouse_id": self.id,
|
||||
"company_id": self.company_id.id,
|
||||
"location_src_id": self.lot_rma_id.id,
|
||||
"location_dest_id": supplier_loc.id,
|
||||
"procure_method": "make_to_stock",
|
||||
"route_id": self.env.ref("rma.route_rma_supplier").id,
|
||||
"picking_type_id": self.rma_sup_out_type_id.id,
|
||||
"active": True,
|
||||
}
|
||||
return rma_rules
|
||||
|
||||
def _create_or_update_rma_pull(self):
|
||||
rule_obj = self.env["stock.rule"]
|
||||
for wh in self:
|
||||
rules_dict = wh.get_rma_rules_dict()
|
||||
if wh.rma_customer_in_pull_id:
|
||||
wh.rma_customer_in_pull_id.write(rules_dict["rma_customer_in"])
|
||||
else:
|
||||
wh.rma_customer_in_pull_id = rule_obj.create(
|
||||
rules_dict["rma_customer_in"]
|
||||
)
|
||||
|
||||
if wh.rma_customer_out_pull_id:
|
||||
wh.rma_customer_out_pull_id.write(rules_dict["rma_customer_out"])
|
||||
else:
|
||||
wh.rma_customer_out_pull_id = rule_obj.create(
|
||||
rules_dict["rma_customer_out"]
|
||||
)
|
||||
|
||||
if wh.rma_supplier_in_pull_id:
|
||||
wh.rma_supplier_in_pull_id.write(rules_dict["rma_supplier_in"])
|
||||
else:
|
||||
wh.rma_supplier_in_pull_id = rule_obj.create(
|
||||
rules_dict["rma_supplier_in"]
|
||||
)
|
||||
|
||||
if wh.rma_supplier_out_pull_id:
|
||||
wh.rma_supplier_out_pull_id.write(rules_dict["rma_supplier_out"])
|
||||
else:
|
||||
wh.rma_supplier_out_pull_id = rule_obj.create(
|
||||
rules_dict["rma_supplier_out"]
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
class StockLocationRoute(models.Model):
|
||||
_inherit = "stock.route"
|
||||
|
||||
rma_selectable = fields.Boolean(string="Selectable on RMA Lines")
|
||||
3
rma/pyproject.toml
Normal file
3
rma/pyproject.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
[build-system]
|
||||
requires = ["whool"]
|
||||
build-backend = "whool.buildapi"
|
||||
42
rma/readme/CONFIGURE.md
Normal file
42
rma/readme/CONFIGURE.md
Normal file
@@ -0,0 +1,42 @@
|
||||
## 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.
|
||||
|
||||
## RMA Approval Policy
|
||||
|
||||
There are two RMA approval policies in product catogories:
|
||||
|
||||
- One step: Always auto-approve RMAs that only contain products within
|
||||
categories with this policy.
|
||||
- Two steps: A RMA order containing a product within a category with
|
||||
this policy will request the RMA manager approval.
|
||||
|
||||
In order to change the approval policy of a product category follow the
|
||||
next steps:
|
||||
|
||||
1. Go to *Inventory \> Configuration \> Products \> Product
|
||||
Categories*.
|
||||
2. Select one and change the field *RMA Approval Policy* to your
|
||||
convenience.
|
||||
|
||||
## Other Settings
|
||||
|
||||
1. Go to RMA \> Configuration \> Settings \> Return Merchandising
|
||||
Authorization and select the option "Display 3 fields on rma:
|
||||
partner, invoice address, delivery address" if needed.
|
||||
2. Go to RMA \> 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 type of operation supplier if we are
|
||||
moving in the company and customer if we are moving out of the
|
||||
company.
|
||||
8
rma/readme/CONTRIBUTORS.md
Normal file
8
rma/readme/CONTRIBUTORS.md
Normal file
@@ -0,0 +1,8 @@
|
||||
- Jordi Ballester Alomar \<<jordi.ballester@forgeflow.com>\>
|
||||
- Aaron Henriquez \<<ahenriquez@forgeflow.com>\>
|
||||
- Lois Rilo \<<lois.rilo@forgeflow.com>\>
|
||||
- Bhavesh Odedra \<<bodedra@opensourceintegrators.com>\>
|
||||
- Akim Juillerat \<<akim.juillerat@camptocamp.com>\>
|
||||
- Alexandre Fayolle \<<alexandre.fayolle@camptocamp.com>\>
|
||||
- Chafique Delli \<<chafique.delli@akretion.com>\>
|
||||
- Héctor Villarreal \<<hector.villarreal@forgeflow.com>\>
|
||||
19
rma/readme/DESCRIPTION.md
Normal file
19
rma/readme/DESCRIPTION.md
Normal file
@@ -0,0 +1,19 @@
|
||||
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.
|
||||
6
rma/readme/ROADMAP.md
Normal file
6
rma/readme/ROADMAP.md
Normal file
@@ -0,0 +1,6 @@
|
||||
- Picking operations report in customer RMA dropshipping case is showing
|
||||
"Vendor Address" while it should be "Customer Address".
|
||||
- Dropshipping always counted as a delivery on the smart buttons.
|
||||
- Uninstall hook.
|
||||
- Constraints instead of required fields on rma.order.line.
|
||||
- Rename type field on rma.order and rma.order.line
|
||||
13
rma/readme/USAGE.md
Normal file
13
rma/readme/USAGE.md
Normal file
@@ -0,0 +1,13 @@
|
||||
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:
|
||||
|
||||
1. Select a partner. Enter RMA lines associated to an existing picking,
|
||||
or manually.
|
||||
2. Request approval and approve.
|
||||
3. Click on RMA Lines button.
|
||||
4. Click on more and select an option: "Receive products", "Create
|
||||
Delivery Order".
|
||||
5. Go back to the RMA. Set the RMA to done if not further action is
|
||||
required.
|
||||
57
rma/report/report_deliveryslip.xml
Normal file
57
rma/report/report_deliveryslip.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<template id="report_delivery_document" inherit_id="stock.report_delivery_document">
|
||||
|
||||
<xpath
|
||||
expr="//table[@name='stock_move_table']//th[@name='th_sm_quantity']"
|
||||
position="before"
|
||||
>
|
||||
<t
|
||||
t-set="has_rma"
|
||||
t-value="bool(o.move_ids.filtered(lambda m: m.rma_line_id))"
|
||||
/>
|
||||
<th t-if="has_rma" name="th_sm_rma_line"><strong>RMA</strong></th>
|
||||
</xpath>
|
||||
<xpath expr="//table[@name='stock_move_table']/tbody//td[2]" position="before">
|
||||
<td t-if="has_rma">
|
||||
<span t-field="move.rma_line_id" />
|
||||
</td>
|
||||
</xpath>
|
||||
|
||||
<xpath
|
||||
expr="//table[@name='stock_move_line_table']//th[@name='th_sml_quantity']"
|
||||
position="before"
|
||||
>
|
||||
<t
|
||||
t-set="sml_has_rma"
|
||||
t-value="bool(o.move_line_ids.filtered(lambda m: m.move_id.rma_line_id))"
|
||||
/>
|
||||
<th t-if="sml_has_rma" name="th_sml_rma_line"><strong>RMA</strong></th>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template
|
||||
id="stock_report_delivery_has_serial_move_line"
|
||||
inherit_id="stock.stock_report_delivery_has_serial_move_line"
|
||||
>
|
||||
<xpath expr="//td[@name='move_line_lot_quantity']" position="before">
|
||||
<td t-if="sml_has_rma">
|
||||
<span t-field="move_line.move_id.rma_line_id" />
|
||||
</td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template
|
||||
id="stock_report_delivery_aggregated_move_lines"
|
||||
inherit_id="stock.stock_report_delivery_aggregated_move_lines"
|
||||
>
|
||||
<xpath expr="//td[@name='move_line_aggregated_quantity']" position="before">
|
||||
<td t-if="sml_has_rma">
|
||||
<!-- TODO: To be implemented.
|
||||
It will require de extension _get_aggregated_product_quantities
|
||||
-->
|
||||
</td>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
24
rma/report/rma_report.xml
Normal file
24
rma/report/rma_report.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="rma_order_line_report" model="ir.actions.report">
|
||||
<field name="name">RMA</field>
|
||||
<field name="model">rma.order.line</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">rma.report_rma_order_line</field>
|
||||
<field name="report_file">rma.report_rma_order_line</field>
|
||||
<field name="print_report_name">('RMA - %s' % (object.name))</field>
|
||||
<field name="binding_model_id" ref="model_rma_order_line" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
<record id="rma_order_report" model="ir.actions.report">
|
||||
<field name="name">RMA Group</field>
|
||||
<field name="model">rma.order</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">rma.report_rma_order</field>
|
||||
<field name="report_file">rma.report_rma_order</field>
|
||||
<field name="print_report_name">('RMA Group - %s' % (object.name))</field>
|
||||
<field name="binding_model_id" ref="model_rma_order" />
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
</odoo>
|
||||
268
rma/report/rma_report_templates.xml
Normal file
268
rma/report/rma_report_templates.xml
Normal file
@@ -0,0 +1,268 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<template id="report_rma_order_line_document">
|
||||
<t t-call="web.external_layout">
|
||||
<t t-set="doc" t-value="doc.with_context({'lang':doc.partner_id.lang})" />
|
||||
<div class="page">
|
||||
<div class="oe_structure" />
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<t
|
||||
t-if="(doc.customer_to_supplier==False and doc.type=='customer') or (doc.supplier_to_customer==False and doc.type=='supplier')"
|
||||
>
|
||||
<strong>Shipping address:</strong>
|
||||
<div class="mt8">
|
||||
<div
|
||||
t-field="doc.in_warehouse_id.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'
|
||||
/>
|
||||
<p t-if="doc.in_warehouse_id.partner_id.vat">VAT: <span
|
||||
t-field="doc.in_warehouse_id.partner_id.vat"
|
||||
/></p>
|
||||
</div>
|
||||
</t>
|
||||
<div
|
||||
t-if="doc.customer_to_supplier and doc.type=='customer'"
|
||||
class="mt8"
|
||||
>
|
||||
<strong>Shipping address:</strong>
|
||||
<div
|
||||
t-field="doc.supplier_address_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'
|
||||
/>
|
||||
<p t-if="doc.supplier_address_id.vat">VAT: <span
|
||||
t-field="doc.supplier_address_id.vat"
|
||||
/></p>
|
||||
</div>
|
||||
<div
|
||||
t-if="doc.supplier_to_customer and doc.type=='supplier'"
|
||||
class="mt8"
|
||||
>
|
||||
<strong>Shipping address:</strong>
|
||||
<div
|
||||
t-field="doc.customer_address_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'
|
||||
/>
|
||||
<p t-if="doc.customer_address_id.vat">VAT: <span
|
||||
t-field="doc.customer_address_id.vat"
|
||||
/></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3 offset-1">
|
||||
<strong t-if="doc.type=='customer'">Customer:</strong>
|
||||
<strong t-if="doc.type=='supplier'">Supplier:</strong>
|
||||
<div
|
||||
t-field="doc.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="mt32">
|
||||
<span t-field="doc.name" />
|
||||
</h2>
|
||||
|
||||
<div class="row mt32 mb32" id="informations">
|
||||
<div t-if="doc.origin" class="col-3">
|
||||
<strong>Origin Reference:</strong>
|
||||
<p t-field="doc.origin" />
|
||||
</div>
|
||||
<div t-if="doc.create_date" class="col-3">
|
||||
<strong>Date Ordered:</strong>
|
||||
<p t-field="doc.create_date" />
|
||||
</div>
|
||||
<div t-if="doc.requested_by.name" class="col-3">
|
||||
<strong>Contact Person:</strong>
|
||||
<p t-field="doc.requested_by.name" />
|
||||
</div>
|
||||
<div name="operation_id" t-if="doc.operation_id" class="col-3">
|
||||
<strong>Operation:</strong>
|
||||
<p t-field="doc.operation_id.name" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th name="product" invisible="1">Product</th>
|
||||
<th name="lot" t-if="doc.lot_id">Lot/Serial Number</th>
|
||||
<th name="quantity" class="text-right">Quantity</th>
|
||||
<th name="unit_price" class="text-right">Unit Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td name="product"><span t-field="doc.product_id" /></td>
|
||||
<td name="lot" t-if="doc.lot_id"><span
|
||||
t-field="doc.lot_id"
|
||||
/></td>
|
||||
<td name="quantity" class="text-right">
|
||||
<span t-field="doc.product_qty" />
|
||||
<span t-field="doc.uom_id" groups="uom.group_uom" />
|
||||
</td>
|
||||
<td name="unit_price" class="text-right">
|
||||
<span
|
||||
t-field="doc.price_unit"
|
||||
t-options='{"widget": "monetary", "display_currency": doc.currency_id}'
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div t-if="doc.description">
|
||||
<strong><p>Description</p></strong>
|
||||
<span t-field="doc.description" />
|
||||
</div>
|
||||
<div t-if="doc.conditions">
|
||||
<strong><p>Term and conditions</p></strong>
|
||||
<span t-field="doc.conditions" />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="report_rma_order_line">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t
|
||||
t-call="rma.report_rma_order_line_document"
|
||||
t-lang="doc.partner_id.lang"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
|
||||
<template id="report_rma_order_document">
|
||||
<t t-call="web.external_layout">
|
||||
<t t-set="doc" t-value="doc.with_context({'lang':doc.partner_id.lang})" />
|
||||
<div class="page">
|
||||
<div class="oe_structure" />
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<t
|
||||
t-if="(doc.customer_to_supplier==False and doc.type=='customer') or (doc.supplier_to_customer==False and doc.type=='supplier')"
|
||||
>
|
||||
<strong>Shipping address:</strong>
|
||||
<div class="mt8">
|
||||
<div
|
||||
t-field="doc.in_warehouse_id.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'
|
||||
/>
|
||||
<p t-if="doc.in_warehouse_id.partner_id.vat">VAT: <span
|
||||
t-field="doc.in_warehouse_id.partner_id.vat"
|
||||
/></p>
|
||||
</div>
|
||||
</t>
|
||||
<div
|
||||
t-if="doc.customer_to_supplier and doc.type=='customer'"
|
||||
class="mt8"
|
||||
>
|
||||
<strong>Shipping address:</strong>
|
||||
<div
|
||||
t-field="doc.supplier_address_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'
|
||||
/>
|
||||
<p t-if="doc.supplier_address_id.vat">VAT: <span
|
||||
t-field="doc.supplier_address_id.vat"
|
||||
/></p>
|
||||
</div>
|
||||
<div
|
||||
t-if="doc.supplier_to_customer and doc.type=='supplier'"
|
||||
class="mt8"
|
||||
>
|
||||
<strong>Shipping address:</strong>
|
||||
<div
|
||||
t-field="doc.customer_address_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": True, "phone_icons": True}'
|
||||
/>
|
||||
<p t-if="doc.customer_address_id.vat">VAT: <span
|
||||
t-field="doc.customer_address_id.vat"
|
||||
/></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3 offset-1">
|
||||
<strong t-if="doc.type=='customer'">Customer:</strong>
|
||||
<strong t-if="doc.type=='supplier'">Supplier:</strong>
|
||||
<div
|
||||
t-field="doc.partner_id"
|
||||
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="mt32">
|
||||
<span t-field="doc.name" />
|
||||
</h2>
|
||||
|
||||
<div class="row mt32 mb32" id="informations">
|
||||
<div t-if="doc.reference" class="col-3">
|
||||
<strong>Reference:</strong>
|
||||
<p t-field="doc.reference" />
|
||||
</div>
|
||||
<div t-if="doc.date_rma" class="col-3">
|
||||
<strong>Date Ordered:</strong>
|
||||
<p t-field="doc.date_rma" />
|
||||
</div>
|
||||
<div t-if="doc.requested_by.name" class="col-3">
|
||||
<strong>Contact Person:</strong>
|
||||
<p t-field="doc.requested_by.name" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-condensed">
|
||||
<t
|
||||
t-set="print_lot_column"
|
||||
t-value="any(l.lot_id for l in doc.rma_line_ids)"
|
||||
/>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Origin</th>
|
||||
<th>Operation</th>
|
||||
<th>Product</th>
|
||||
<th
|
||||
name="lot"
|
||||
t-if="print_lot_column"
|
||||
>Lot/Serial Number</th>
|
||||
<th class="text-right">Quantity</th>
|
||||
<th class="text-right">Unit Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="doc.rma_line_ids" t-as="l">
|
||||
<td><span t-field="l.origin" /></td>
|
||||
<td><span t-field="l.operation_id.name" /></td>
|
||||
<td><span t-field="l.product_id" /></td>
|
||||
<td name="lot" t-if="print_lot_column"><span
|
||||
t-field="l.lot_id"
|
||||
/></td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.product_qty" />
|
||||
<span t-field="l.uom_id" groups="uom.group_uom" />
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.price_unit" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div t-if="doc.comment">
|
||||
<strong><p>Additional Information</p></strong>
|
||||
<span t-field="doc.comment" />
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_rma_order">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="doc">
|
||||
<t
|
||||
t-call="rma.report_rma_order_document"
|
||||
t-lang="doc.partner_id.lang"
|
||||
/>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
25
rma/security/ir.model.access.csv
Normal file
25
rma/security/ir.model.access.csv
Normal file
@@ -0,0 +1,25 @@
|
||||
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_rma_order_line_user,access_rma_order_line,model_rma_order_line,base.group_user,1,0,0,0
|
||||
access_rma_picking_wizard,rma.order.manager,model_rma_make_picking_wizard,group_rma_manager,1,1,1,1
|
||||
access_rma_picking_wizard_customer,rma.order.manager,model_rma_make_picking_wizard,group_rma_customer_user,1,1,1,1
|
||||
access_rma_picking_wizard_supplier,rma.order.manager,model_rma_make_picking_wizard,group_rma_supplier_user,1,1,1,1
|
||||
access_rma_picking_wizard_item,rma.order.manager,model_rma_make_picking_wizard_item,group_rma_manager,1,1,1,1
|
||||
access_rma_picking_wizard_item_customer,rma.order.manager,model_rma_make_picking_wizard_item,group_rma_customer_user,1,1,1,1
|
||||
access_rma_picking_wizard_item_supplier,rma.order.manager,model_rma_make_picking_wizard_item,group_rma_supplier_user,1,1,1,1
|
||||
access_rma_order_line_make_supplier_rma_customer_user,rma.order.line.make.supplier.rma.customer.user,model_rma_order_line_make_supplier_rma,rma.group_rma_customer_user,1,1,1,1
|
||||
access_rma_order_line_make_supplier_rmasupplier_user,rma.order.line.make.supplier.rma.supplier.user,model_rma_order_line_make_supplier_rma,rma.group_rma_supplier_user,1,1,1,1
|
||||
access_rma_order_line_make_supplier_rma_customer_user_item,rma.order.line.make.supplier.rma.item.customer.user,model_rma_order_line_make_supplier_rma_item,rma.group_rma_customer_user,1,1,1,1
|
||||
access_rma_order_line_make_supplier_rmasupplier_user_item,rma.order.line.make.supplier.rma.item.supplier.user,model_rma_order_line_make_supplier_rma_item,rma.group_rma_supplier_user,1,1,1,1
|
||||
access_rma_add_stock_move_customer_user_item,rma.add.stock.move.customer.user,model_rma_add_stock_move,rma.group_rma_customer_user,1,1,1,1
|
||||
access_rma_add_stock_move_supplier_user_item,rma.add.stock.move.supplier.user,model_rma_add_stock_move,rma.group_rma_supplier_user,1,1,1,1
|
||||
access_rma_add_serial_wiz_customer,access_rma_add_serial_wiz_customer,model_rma_add_serial_wiz,rma.group_rma_customer_user,1,1,1,1
|
||||
access_rma_add_serial_wiz_supplier,access_rma_add_serial_wiz_supplier,model_rma_add_serial_wiz,rma.group_rma_supplier_user,1,1,1,1
|
||||
|
85
rma/security/rma.xml
Normal file
85
rma/security/rma.xml
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo 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" />
|
||||
<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('stock.group_stock_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('stock.group_stock_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>
|
||||
|
||||
<record id="group_rma_groups" model="res.groups">
|
||||
<field name="name">RMA Groups</field>
|
||||
<field name="category_id" ref="module_category_rma" />
|
||||
</record>
|
||||
|
||||
<record id="stock.group_stock_manager" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('group_rma_manager'))]" />
|
||||
</record>
|
||||
|
||||
<record model="ir.rule" id="rma_order_rule">
|
||||
<field name="name">rma order multi-company</field>
|
||||
<field
|
||||
name="model_id"
|
||||
search="[('model','=','rma.order')]"
|
||||
model="ir.model"
|
||||
/>
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record model="ir.rule" id="rma_order_line_rule">
|
||||
<field name="name">rma order line multi-company</field>
|
||||
<field
|
||||
name="model_id"
|
||||
search="[('model','=','rma.order.line')]"
|
||||
model="ir.model"
|
||||
/>
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
<record model="ir.rule" id="rma_operation_rule">
|
||||
<field name="name">rma operation multi-company</field>
|
||||
<field
|
||||
name="model_id"
|
||||
search="[('model','=','rma.operation')]"
|
||||
model="ir.model"
|
||||
/>
|
||||
<field name="global" eval="True" />
|
||||
<field
|
||||
name="domain_force"
|
||||
>['|', ('company_id', '=', False), ('company_id', 'in', company_ids)]</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
BIN
rma/static/description/icon.png
Normal file
BIN
rma/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
520
rma/static/description/index.html
Normal file
520
rma/static/description/index.html
Normal file
@@ -0,0 +1,520 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
|
||||
<title>RMA (Return Merchandise Authorization)</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: grey; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document" id="rma-return-merchandise-authorization">
|
||||
<h1 class="title">RMA (Return Merchandise Authorization)</h1>
|
||||
|
||||
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:310ce4e3f5919e5be643c54f11481d09bb8fc79cab65f08ac969ef3bf9d799fe
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/ForgeFlow/stock-rma/tree/17.0/rma"><img alt="ForgeFlow/stock-rma" src="https://img.shields.io/badge/github-ForgeFlow%2Fstock--rma-lightgray.png?logo=github" /></a></p>
|
||||
<p>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.</p>
|
||||
<p>The purchaser of the product must contact the manufacturer (or
|
||||
distributor or retailer) to obtain authorization to return the product.</p>
|
||||
<p>The resulting RMA number must be displayed on or included in the
|
||||
returned product’s packaging.</p>
|
||||
<p>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.</p>
|
||||
<p>As returns are costly for the vendor and inconvenient for the customer,
|
||||
any return that can be prevented benefits both parties.</p>
|
||||
<p><strong>Table of contents</strong></p>
|
||||
<div class="contents local topic" id="contents">
|
||||
<ul class="simple">
|
||||
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a><ul>
|
||||
<li><a class="reference internal" href="#security" id="toc-entry-2">Security</a></li>
|
||||
<li><a class="reference internal" href="#rma-approval-policy" id="toc-entry-3">RMA Approval Policy</a></li>
|
||||
<li><a class="reference internal" href="#other-settings" id="toc-entry-4">Other Settings</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference internal" href="#usage" id="toc-entry-5">Usage</a></li>
|
||||
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-6">Known issues / Roadmap</a></li>
|
||||
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-7">Bug Tracker</a></li>
|
||||
<li><a class="reference internal" href="#credits" id="toc-entry-8">Credits</a><ul>
|
||||
<li><a class="reference internal" href="#authors" id="toc-entry-9">Authors</a></li>
|
||||
<li><a class="reference internal" href="#contributors" id="toc-entry-10">Contributors</a></li>
|
||||
<li><a class="reference internal" href="#maintainers" id="toc-entry-11">Maintainers</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="configuration">
|
||||
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
|
||||
<div class="section" id="security">
|
||||
<h2><a class="toc-backref" href="#toc-entry-2">Security</a></h2>
|
||||
<p>Go to Settings > Users and assign the appropiate permissions to users.
|
||||
Different security groups grant distinct levels of access to the RMA
|
||||
features.</p>
|
||||
<ul class="simple">
|
||||
<li>Users in group “RMA Customer User” or “RMA Supplier User” can access
|
||||
to, create and process RMA’s associated to customers or suppliers
|
||||
respectively.</li>
|
||||
<li>Users in group “RMA Manager” can access to, create, approve and
|
||||
process RMA’s associated to both customers and suppliers.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="rma-approval-policy">
|
||||
<h2><a class="toc-backref" href="#toc-entry-3">RMA Approval Policy</a></h2>
|
||||
<p>There are two RMA approval policies in product catogories:</p>
|
||||
<ul class="simple">
|
||||
<li>One step: Always auto-approve RMAs that only contain products within
|
||||
categories with this policy.</li>
|
||||
<li>Two steps: A RMA order containing a product within a category with
|
||||
this policy will request the RMA manager approval.</li>
|
||||
</ul>
|
||||
<p>In order to change the approval policy of a product category follow the
|
||||
next steps:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to <em>Inventory > Configuration > Products > Product Categories</em>.</li>
|
||||
<li>Select one and change the field <em>RMA Approval Policy</em> to your
|
||||
convenience.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="other-settings">
|
||||
<h2><a class="toc-backref" href="#toc-entry-4">Other Settings</a></h2>
|
||||
<ol class="arabic simple">
|
||||
<li>Go to RMA > Configuration > Settings > Return Merchandising
|
||||
Authorization and select the option “Display 3 fields on rma:
|
||||
partner, invoice address, delivery address” if needed.</li>
|
||||
<li>Go to RMA > 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 type of operation supplier if we are
|
||||
moving in the company and customer if we are moving out of the
|
||||
company.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="usage">
|
||||
<h1><a class="toc-backref" href="#toc-entry-5">Usage</a></h1>
|
||||
<p>RMA are accessible though Inventory menu. There’s four menus, divided by
|
||||
type. Users can access to the list of RMA or RMA lines.</p>
|
||||
<p>Create an RMA:</p>
|
||||
<ol class="arabic simple">
|
||||
<li>Select a partner. Enter RMA lines associated to an existing picking,
|
||||
or manually.</li>
|
||||
<li>Request approval and approve.</li>
|
||||
<li>Click on RMA Lines button.</li>
|
||||
<li>Click on more and select an option: “Receive products”, “Create
|
||||
Delivery Order”.</li>
|
||||
<li>Go back to the RMA. Set the RMA to done if not further action is
|
||||
required.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="known-issues-roadmap">
|
||||
<h1><a class="toc-backref" href="#toc-entry-6">Known issues / Roadmap</a></h1>
|
||||
<ul class="simple">
|
||||
<li>Picking operations report in customer RMA dropshipping case is
|
||||
showing “Vendor Address” while it should be “Customer Address”.</li>
|
||||
<li>Dropshipping always counted as a delivery on the smart buttons.</li>
|
||||
<li>Uninstall hook.</li>
|
||||
<li>Constraints instead of required fields on rma.order.line.</li>
|
||||
<li>Rename type field on rma.order and rma.order.line</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="bug-tracker">
|
||||
<h1><a class="toc-backref" href="#toc-entry-7">Bug Tracker</a></h1>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/ForgeFlow/stock-rma/issues">GitHub Issues</a>.
|
||||
In case of trouble, please check there if your issue has already been reported.
|
||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/ForgeFlow/stock-rma/issues/new?body=module:%20rma%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<p>Do not contact contributors directly about support or help with technical issues.</p>
|
||||
</div>
|
||||
<div class="section" id="credits">
|
||||
<h1><a class="toc-backref" href="#toc-entry-8">Credits</a></h1>
|
||||
<div class="section" id="authors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-9">Authors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>ForgeFlow</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="contributors">
|
||||
<h2><a class="toc-backref" href="#toc-entry-10">Contributors</a></h2>
|
||||
<ul class="simple">
|
||||
<li>Jordi Ballester Alomar <<a class="reference external" href="mailto:jordi.ballester@forgeflow.com">jordi.ballester@forgeflow.com</a>></li>
|
||||
<li>Aaron Henriquez <<a class="reference external" href="mailto:ahenriquez@forgeflow.com">ahenriquez@forgeflow.com</a>></li>
|
||||
<li>Lois Rilo <<a class="reference external" href="mailto:lois.rilo@forgeflow.com">lois.rilo@forgeflow.com</a>></li>
|
||||
<li>Bhavesh Odedra <<a class="reference external" href="mailto:bodedra@opensourceintegrators.com">bodedra@opensourceintegrators.com</a>></li>
|
||||
<li>Akim Juillerat <<a class="reference external" href="mailto:akim.juillerat@camptocamp.com">akim.juillerat@camptocamp.com</a>></li>
|
||||
<li>Alexandre Fayolle <<a class="reference external" href="mailto:alexandre.fayolle@camptocamp.com">alexandre.fayolle@camptocamp.com</a>></li>
|
||||
<li>Chafique Delli <<a class="reference external" href="mailto:chafique.delli@akretion.com">chafique.delli@akretion.com</a>></li>
|
||||
<li>Héctor Villarreal <<a class="reference external" href="mailto:hector.villarreal@forgeflow.com">hector.villarreal@forgeflow.com</a>></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section" id="maintainers">
|
||||
<h2><a class="toc-backref" href="#toc-entry-11">Maintainers</a></h2>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/ForgeFlow/stock-rma/tree/17.0/rma">ForgeFlow/stock-rma</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
4
rma/tests/__init__.py
Normal file
4
rma/tests/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from . import test_rma
|
||||
1099
rma/tests/test_rma.py
Normal file
1099
rma/tests/test_rma.py
Normal file
File diff suppressed because it is too large
Load Diff
54
rma/views/product_view.xml
Normal file
54
rma/views/product_view.xml
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<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="stock.product_category_form_view_inherit" />
|
||||
<field name="arch" type="xml">
|
||||
<group name="logistics" position="after">
|
||||
<group name="rma">
|
||||
<field
|
||||
name="rma_approval_policy"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
/>
|
||||
<field
|
||||
name="rma_customer_operation_id"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
domain="[('type','=','customer')]"
|
||||
/>
|
||||
<field
|
||||
name="rma_supplier_operation_id"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
domain="[('type','=','supplier')]"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</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_approval_policy" />
|
||||
<field
|
||||
name="rma_customer_operation_id"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
domain="[('type','=','customer')]"
|
||||
/>
|
||||
<field
|
||||
name="rma_supplier_operation_id"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
domain="[('type','=','supplier')]"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
74
rma/views/res_config_settings_views.xml
Normal file
74
rma/views/res_config_settings_views.xml
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="res_config_settings_view_form" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.inherit.rma</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="priority" eval="10" />
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<div
|
||||
class="app_settings_block"
|
||||
data-string="RMA"
|
||||
string="RMA"
|
||||
data-key="rma"
|
||||
groups="rma.group_rma_manager"
|
||||
>
|
||||
<h2>Return Merchandise Authorization</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div
|
||||
name="rma_delivery_address"
|
||||
class="col-12 col-lg-6 o_setting_box"
|
||||
>
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_rma_delivery_address" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="group_rma_delivery_address" />
|
||||
<div class="text-muted">
|
||||
Display 3 fields on rma: partner, invoice address, delivery address
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
name="group_rma_lines"
|
||||
class="col-12 col-lg-6 o_setting_box"
|
||||
>
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="group_rma_lines" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="group_rma_lines" />
|
||||
<div class="text-muted">
|
||||
Group RMA lines in one RMA group
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div name="rma_account" class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="module_rma_account" />
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="module_rma_account" />
|
||||
<div class="text-muted">
|
||||
Enable RMA invoicing
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_rma_config_settings" model="ir.actions.act_window">
|
||||
<field name="name">Settings</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.config.settings</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">inline</field>
|
||||
<field name="context">{'module' : 'rma'}</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
28
rma/views/res_partner_view.xml
Normal file
28
rma/views/res_partner_view.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_partner_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.form - rma</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box" position="inside">
|
||||
<button
|
||||
type="object"
|
||||
name="action_open_partner_rma"
|
||||
class="oe_stat_button"
|
||||
icon="fa-dropbox"
|
||||
context="{'res_partner_search_mode': 'customer'}"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
>
|
||||
<field
|
||||
name="rma_line_count"
|
||||
widget="statinfo"
|
||||
string="RMAs"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
148
rma/views/rma_menu.xml
Normal file
148
rma/views/rma_menu.xml
Normal file
@@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_root"
|
||||
name="RMA"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user,rma.group_rma_manager"
|
||||
web_icon="rma,static/description/icon.png"
|
||||
sequence="100"
|
||||
/>
|
||||
|
||||
<!-- Customer RMA -->
|
||||
<menuitem
|
||||
id="menu_customer_rma"
|
||||
name="Customer RMA"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
sequence="30"
|
||||
parent="menu_rma_root"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_act_customer"
|
||||
sequence="50"
|
||||
parent="menu_customer_rma"
|
||||
groups="rma.group_rma_groups"
|
||||
action="action_rma_customer"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_line_customer"
|
||||
sequence="20"
|
||||
parent="menu_customer_rma"
|
||||
groups="rma.group_rma_customer_user"
|
||||
action="action_rma_customer_lines"
|
||||
/>
|
||||
|
||||
<!-- Supplier RMA -->
|
||||
<menuitem
|
||||
id="menu_supplier_rma"
|
||||
name="Supplier RMA"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
sequence="31"
|
||||
parent="menu_rma_root"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_act_supplier"
|
||||
sequence="50"
|
||||
parent="menu_supplier_rma"
|
||||
groups="rma.group_rma_groups"
|
||||
action="action_rma_supplier"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_line_supplier"
|
||||
sequence="20"
|
||||
parent="menu_supplier_rma"
|
||||
groups="rma.group_rma_supplier_user"
|
||||
action="action_rma_supplier_lines"
|
||||
/>
|
||||
|
||||
<!-- Settings-->
|
||||
<menuitem
|
||||
id="menu_rma_config"
|
||||
name="Configuration"
|
||||
groups="rma.group_rma_manager"
|
||||
sequence="999"
|
||||
parent="menu_rma_root"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_global_settings"
|
||||
name="Settings"
|
||||
parent="rma.menu_rma_config"
|
||||
sequence="1"
|
||||
action="action_rma_config_settings"
|
||||
groups="base.group_system"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_operation_customer"
|
||||
name="Customer Operations"
|
||||
groups="rma.group_rma_manager"
|
||||
sequence="35"
|
||||
parent="rma.menu_rma_config"
|
||||
action="action_rma_operation_customer"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_operation_supplier"
|
||||
name="Supplier Operations"
|
||||
groups="rma.group_rma_manager"
|
||||
sequence="40"
|
||||
parent="rma.menu_rma_config"
|
||||
action="action_rma_operation_supplier"
|
||||
/>
|
||||
|
||||
<!-- Master data menus-->
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_rma_control"
|
||||
name="Master Data"
|
||||
parent="menu_rma_root"
|
||||
sequence="50"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_rma_config_warehouse"
|
||||
name="Warehouse"
|
||||
parent="rma.menu_rma_rma_control"
|
||||
sequence="10"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_rma_config_stock"
|
||||
name="Products"
|
||||
action="stock.product_template_action_product"
|
||||
parent="rma.menu_rma_config_warehouse"
|
||||
sequence="10"
|
||||
/>
|
||||
<menuitem
|
||||
id="rma_product_product_menu"
|
||||
name="Product Variants"
|
||||
action="stock.stock_product_normal_action"
|
||||
parent="rma.menu_rma_config_warehouse"
|
||||
sequence="20"
|
||||
groups="product.group_product_variant"
|
||||
/>
|
||||
|
||||
<menuitem
|
||||
id="menu_rma_config_partners"
|
||||
name="Partners"
|
||||
parent="rma.menu_rma_rma_control"
|
||||
sequence="20"
|
||||
/>
|
||||
<menuitem
|
||||
id="rma_res_partner_menu_customer"
|
||||
name="Customers"
|
||||
parent="rma.menu_rma_config_partners"
|
||||
action="base.action_partner_form"
|
||||
sequence="50"
|
||||
/>
|
||||
<menuitem
|
||||
id="rma_res_partner_menu_suppliers"
|
||||
name="Suppliers"
|
||||
parent="rma.menu_rma_config_partners"
|
||||
action="base.action_partner_supplier_form"
|
||||
sequence="60"
|
||||
/>
|
||||
</odoo>
|
||||
89
rma/views/rma_operation_view.xml
Normal file
89
rma/views/rma_operation_view.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="rma_operation_tree" model="ir.ui.view">
|
||||
<field name="name">rma.operation.tree</field>
|
||||
<field name="model">rma.operation</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree name="RMA Operations">
|
||||
<field name="active" invisible="1" />
|
||||
<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">
|
||||
<sheet>
|
||||
<widget
|
||||
name="web_ribbon"
|
||||
title="Archived"
|
||||
bg_color="bg-danger"
|
||||
invisible="active == True"
|
||||
/>
|
||||
<div class="oe_button_box" name="button_box" />
|
||||
<group colspan="4" col="2">
|
||||
<group name="description" string="Description">
|
||||
<field name="active" invisible="1" />
|
||||
<field name="code" />
|
||||
<field name="name" />
|
||||
<field name="type" />
|
||||
<field
|
||||
name="company_id"
|
||||
groups="base.group_multi_company"
|
||||
/>
|
||||
</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"
|
||||
invisible="type == 'supplier'"
|
||||
/>
|
||||
</group>
|
||||
<group name="outbound" string="Outbound">
|
||||
<field name="out_route_id" />
|
||||
<field name="out_warehouse_id" />
|
||||
<field
|
||||
name="supplier_to_customer"
|
||||
invisible="type == 'customer'"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_rma_operation_customer" model="ir.actions.act_window">
|
||||
<field name="name">Customer Operations</field>
|
||||
<field name="res_model">rma.operation</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'default_type': "customer"}</field>
|
||||
<field name="domain">[('type','=', 'customer')]</field>
|
||||
<field name="view_id" ref="rma_operation_tree" />
|
||||
</record>
|
||||
|
||||
<record id="action_rma_operation_supplier" model="ir.actions.act_window">
|
||||
<field name="name">Supplier Operations</field>
|
||||
<field name="res_model">rma.operation</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'default_type': "supplier"}</field>
|
||||
<field name="domain">[('type','=', 'supplier')]</field>
|
||||
<field name="view_id" ref="rma_operation_tree" />
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
547
rma/views/rma_order_line_view.xml
Normal file
547
rma/views/rma_order_line_view.xml
Normal file
@@ -0,0 +1,547 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<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 name="RMA Line" decoration-info="state in ('draft','to_approve')">
|
||||
<field name="name" />
|
||||
<field name="partner_id" />
|
||||
<field name="product_id" />
|
||||
<field
|
||||
name="lot_id"
|
||||
groups="stock.group_production_lot"
|
||||
context="{'default_product_id': product_id,}"
|
||||
/>
|
||||
<field name="rma_id" groups="rma.group_rma_groups" />
|
||||
<field name="origin" />
|
||||
<field name="date_rma" />
|
||||
<field name="operation_id" />
|
||||
<field name="supplier_address_id" />
|
||||
<field name="uom_id" groups="uom.group_uom" />
|
||||
<field name="product_qty" />
|
||||
<field name="price_unit" />
|
||||
<field name="state" />
|
||||
</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 name="RMA Line" decoration-info="state in ('draft','to_approve')">
|
||||
<field name="name" />
|
||||
<field name="partner_id" />
|
||||
<field name="product_id" />
|
||||
<field
|
||||
name="lot_id"
|
||||
groups="stock.group_production_lot"
|
||||
context="{'default_product_id': product_id,}"
|
||||
/>
|
||||
<field name="rma_id" groups="rma.group_rma_groups" />
|
||||
<field name="origin" />
|
||||
<field name="date_rma" />
|
||||
<field name="operation_id" domain="[('type','=','supplier')]" />
|
||||
<field name="uom_id" groups="uom.group_uom" />
|
||||
<field name="product_qty" />
|
||||
<field name="price_unit" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</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">
|
||||
<header>
|
||||
<button
|
||||
name="action_rma_to_approve"
|
||||
type="object"
|
||||
string="Request Approval"
|
||||
invisible="state != 'draft'"
|
||||
class="oe_highlight"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_draft"
|
||||
type="object"
|
||||
string="Back to Draft"
|
||||
invisible="state == 'draft'"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_approve"
|
||||
type="object"
|
||||
string="Approve"
|
||||
invisible="state != 'to_approve'"
|
||||
class="oe_highlight"
|
||||
groups="rma.group_rma_manager"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_approve"
|
||||
type="object"
|
||||
string="Back to Approved"
|
||||
invisible="state != 'done'"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_done"
|
||||
type="object"
|
||||
string="Done"
|
||||
invisible="state in ('done', 'draft', 'canceled')"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_cancel"
|
||||
type="object"
|
||||
string="Cancel"
|
||||
invisible="state in ('done', 'canceled')"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<field
|
||||
name="state"
|
||||
widget="statusbar"
|
||||
statusbar_visible="draft,to_approve,approved,done"
|
||||
nolabel="1"
|
||||
/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div name="button_box" class="oe_button_box">
|
||||
<button
|
||||
type="object"
|
||||
name="action_view_in_shipments"
|
||||
class="oe_stat_button"
|
||||
icon="fa-truck"
|
||||
invisible="in_shipment_count == 0"
|
||||
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"
|
||||
invisible="out_shipment_count == 0"
|
||||
groups="stock.group_stock_user"
|
||||
>
|
||||
<field
|
||||
name="out_shipment_count"
|
||||
widget="statinfo"
|
||||
string="Deliveries"
|
||||
/>
|
||||
</button>
|
||||
<!--Move this button to rma_account-->
|
||||
<!--<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_rma_lines"
|
||||
class="oe_stat_button"
|
||||
icon="fa-link"
|
||||
groups="stock.group_stock_user"
|
||||
invisible="rma_line_count == 0"
|
||||
>
|
||||
<field
|
||||
name="rma_line_count"
|
||||
widget="statinfo"
|
||||
string="Supplier RMA"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title" name="title">
|
||||
<h1>
|
||||
<field
|
||||
name="name"
|
||||
readonly="state not in ['draft']"
|
||||
invisible="context.get('hide_title',False)"
|
||||
/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group name="partner">
|
||||
<field
|
||||
name="partner_id"
|
||||
context="{'res_partner_search_mode': 'customer'}"
|
||||
string="Customer"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="delivery_address_id"
|
||||
domain="['|', ('parent_id', '=', partner_id),
|
||||
('id', '=', partner_id)]"
|
||||
context="{'show_address': 1}"
|
||||
options="{'always_reload': 1}"
|
||||
groups='rma.group_rma_delivery_invoice_address'
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<group name="contact">
|
||||
<field name="date_rma" />
|
||||
<field name="requested_by" readonly="1" />
|
||||
<field name="assigned_to" />
|
||||
<field name="type" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group name="product" string="Product">
|
||||
<field
|
||||
name="product_id"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<newline />
|
||||
<field name="product_tracking" invisible="1" />
|
||||
<field
|
||||
name="lot_id"
|
||||
groups="stock.group_production_lot"
|
||||
required="product_tracking in ('serial', 'lot')"
|
||||
context="{'default_product_id': product_id,}"
|
||||
domain="[('product_id', '=', product_id)]"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="under_warranty"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
</group>
|
||||
<group name="product_qty" string="Quantity">
|
||||
<field
|
||||
name="product_qty"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="uom_id"
|
||||
groups="uom.group_uom"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="price_unit"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="main_info" string="Origin">
|
||||
<field
|
||||
name="reference_move_id"
|
||||
options="{'no_create': True}"
|
||||
domain="[('picking_id.partner_id', '=', partner_id),
|
||||
('location_dest_id.usage', '=', 'customer'),
|
||||
('state', '=', 'done')]"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
</group>
|
||||
<group>
|
||||
<group name="operation" string="Operation">
|
||||
<field
|
||||
name="operation_id"
|
||||
domain="[('type','=','customer')]"
|
||||
/>
|
||||
<field name="receipt_policy" />
|
||||
<field name="delivery_policy" />
|
||||
</group>
|
||||
<group name="description" string="Description">
|
||||
<field name="description" nolabel="1" colspan="4" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page name="quantities" string="Quantities">
|
||||
<group name="quantities" col="4" string="Quantities">
|
||||
<group
|
||||
name="receive"
|
||||
invisible="receipt_policy == 'no'"
|
||||
>
|
||||
<field name="qty_to_receive" />
|
||||
<field name="qty_incoming" />
|
||||
<field name="qty_received" />
|
||||
</group>
|
||||
<group
|
||||
name="deliver"
|
||||
invisible="delivery_policy == 'no'"
|
||||
>
|
||||
<field name="qty_to_deliver" />
|
||||
<field name="qty_outgoing" />
|
||||
<field name="qty_delivered" />
|
||||
</group>
|
||||
<group
|
||||
name="supplier_rma"
|
||||
invisible="type != 'customer' or not customer_to_supplier"
|
||||
>
|
||||
<field name="qty_to_supplier_rma" />
|
||||
<field name="qty_in_supplier_rma" />
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page name="route" string="Routes">
|
||||
<group name="inbound" string="Inbound">
|
||||
<field
|
||||
name="in_warehouse_id"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="location_id"
|
||||
domain="[('usage', '=', 'internal')]"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="in_route_id"
|
||||
groups="stock.group_adv_location"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="customer_to_supplier"
|
||||
invisible="type != 'customer'"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="supplier_address_id"
|
||||
context="{'show_address': 1}"
|
||||
options="{'always_reload': 1}"
|
||||
required="customer_to_supplier == True"
|
||||
invisible="customer_to_supplier == False"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
</group>
|
||||
<group name="outbound" string="Outbound">
|
||||
<field
|
||||
name="out_warehouse_id"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="out_route_id"
|
||||
groups="stock.group_adv_location"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="supplier_to_customer"
|
||||
invisible="type != 'supplier'"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
<field
|
||||
name="customer_address_id"
|
||||
context="{'show_address': 1}"
|
||||
options="{'always_reload': 1}"
|
||||
required="supplier_to_customer == True"
|
||||
invisible="supplier_to_customer == False"
|
||||
readonly="state not in ['draft']"
|
||||
/>
|
||||
</group>
|
||||
</page>
|
||||
<page
|
||||
name="stock"
|
||||
string="Stock Moves"
|
||||
invisible="not move_ids"
|
||||
>
|
||||
<field name="move_ids" nolabel="1" readonly="1" />
|
||||
</page>
|
||||
<page name="other" string="Other Info">
|
||||
<group name="general" string="General">
|
||||
<field name="conditions" />
|
||||
<field
|
||||
name="rma_id"
|
||||
groups="rma.group_rma_groups"
|
||||
/>
|
||||
<field name="origin" />
|
||||
</group>
|
||||
<group
|
||||
name="company"
|
||||
string="Company"
|
||||
groups="base.group_multi_company"
|
||||
>
|
||||
<field name="company_id" />
|
||||
</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_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="inherit_id" ref="rma.view_rma_line_form" />
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath
|
||||
expr="//button[@name='action_view_rma_lines']/field"
|
||||
position="attributes"
|
||||
>
|
||||
<attribute name="string">Customer RMA</attribute>
|
||||
</xpath>
|
||||
<field name="partner_id" position="attributes">
|
||||
<attribute name="string">Supplier</attribute>
|
||||
<attribute
|
||||
name="context"
|
||||
>{'res_partner_search_mode': 'supplier'}</attribute>
|
||||
</field>
|
||||
<field name="under_warranty" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</field>
|
||||
<field name="reference_move_id" position="attributes">
|
||||
<attribute
|
||||
name="domain"
|
||||
>[('picking_id.partner_id', '=', partner_id),
|
||||
('location_id.usage', '=', 'supplier'),
|
||||
('state', '=', 'done')]</attribute>
|
||||
</field>
|
||||
<field name="operation_id" position="attributes">
|
||||
<attribute name="domain">[('type','=','supplier')]</attribute>
|
||||
</field>
|
||||
</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="name" />
|
||||
<field name="rma_id" groups="rma.group_rma_groups" />
|
||||
<field name="partner_id" />
|
||||
<field name="requested_by" />
|
||||
<field name="assigned_to" />
|
||||
<field name="product_id" />
|
||||
<field name="lot_id" />
|
||||
<field name="date_rma" />
|
||||
<separator />
|
||||
<filter
|
||||
name="assigned_to_filter"
|
||||
domain="[('assigned_to','=',uid)]"
|
||||
help="My RMAs"
|
||||
/>
|
||||
<separator />
|
||||
<group name="stock_quantities" groups="stock.group_stock_user">
|
||||
<filter
|
||||
name="to_receive"
|
||||
domain="[('state','!=', 'done'),('qty_to_receive','>',0.0)]"
|
||||
help="To Receive"
|
||||
/>
|
||||
<filter
|
||||
name="to_deliver"
|
||||
domain="[('state','!=', 'done'),('qty_to_deliver','>',0.0)]"
|
||||
help="To Deliver"
|
||||
/>
|
||||
</group>
|
||||
<group name="rma_supplier_quantities">
|
||||
<filter
|
||||
name="to_send_to_supplier"
|
||||
domain="[('state','!=', 'done'),('qty_to_supplier_rma','>',0.0)]"
|
||||
help="To Send to Supplier RMA"
|
||||
/>
|
||||
</group>
|
||||
<group expand="0" string="Group By">
|
||||
<filter
|
||||
name="status"
|
||||
string="State"
|
||||
domain="[]"
|
||||
context="{'group_by':'state'}"
|
||||
/>
|
||||
<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</field>
|
||||
<field name="res_model">rma.order.line</field>
|
||||
<field name="domain">[('type','=', 'customer')]</field>
|
||||
<field name="context">{"search_default_assigned_to_filter":1}</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</field>
|
||||
<field name="res_model">rma.order.line</field>
|
||||
<field name="domain">[('type','=', 'supplier')]</field>
|
||||
<field
|
||||
name="context"
|
||||
>{"search_default_assigned_to_filter":1, "supplier":1}</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>
|
||||
|
||||
<record model="ir.actions.server" id="action_request_approve_rma_order_line">
|
||||
<field name="name">Request Approval</field>
|
||||
<field name="model_id" ref="rma.model_rma_order_line" />
|
||||
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
if records.filtered(lambda x: x.state == "draft"):
|
||||
records.filtered(lambda x: x.state == "draft").action_rma_to_approve()
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.server" id="action_request_cancel_rma_order_line">
|
||||
<field name="name">Cancel</field>
|
||||
<field name="model_id" ref="rma.model_rma_order_line" />
|
||||
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
records.action_rma_cancel()
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
380
rma/views/rma_order_view.xml
Normal file
380
rma/views/rma_order_view.xml
Normal file
@@ -0,0 +1,380 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<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 name="RMA">
|
||||
<field name="name" />
|
||||
<field name="reference" />
|
||||
<field name="partner_id" groups="base.group_user" string="Customer" />
|
||||
<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 name="RMA">
|
||||
<field name="name" />
|
||||
<field name="reference" />
|
||||
<field name="partner_id" groups="base.group_user" string="Supplier" />
|
||||
<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">
|
||||
<header>
|
||||
<button
|
||||
name="action_rma_to_approve"
|
||||
type="object"
|
||||
string="Request Approval"
|
||||
invisible="state != 'draft'"
|
||||
class="oe_highlight"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_draft"
|
||||
type="object"
|
||||
string="Back to Draft"
|
||||
invisible="state == 'draft'"
|
||||
groups="rma.group_rma_customer_user,rma.group_rma_supplier_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_approve"
|
||||
type="object"
|
||||
string="Approve"
|
||||
invisible="state != 'to_approve'"
|
||||
class="oe_highlight"
|
||||
groups="rma.group_rma_manager"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_approve"
|
||||
type="object"
|
||||
string="Back to Approved"
|
||||
invisible="state != 'done'"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_done"
|
||||
type="object"
|
||||
string="Done"
|
||||
invisible="state in ('done', 'draft', 'cancel')"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<button
|
||||
name="action_rma_cancel"
|
||||
type="object"
|
||||
string="Cancel"
|
||||
invisible="state in ('done', 'cancel')"
|
||||
groups="rma.group_rma_customer_user"
|
||||
/>
|
||||
<field
|
||||
name="state"
|
||||
widget="statusbar"
|
||||
statusbar_visible="draft,to_approve,approved,done"
|
||||
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"
|
||||
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" readonly="1" />
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group name="info">
|
||||
<field name="type" readonly="1" invisible="1" />
|
||||
<field
|
||||
name="partner_id"
|
||||
context="{'res_partner_search_mode': 'customer'}"
|
||||
string="Customer"
|
||||
/>
|
||||
<field name="reference" invisible="type != 'supplier'" />
|
||||
<field name="date_rma" readonly="state != 'draft'" />
|
||||
</group>
|
||||
<group name="contact">
|
||||
<field name="requested_by" readonly="1" />
|
||||
<field name="assigned_to" readonly="state != 'draft'" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group name="inbound_route" string="Inbound">
|
||||
<field
|
||||
name="operation_default_id"
|
||||
domain="[('type','=','customer')]"
|
||||
/>
|
||||
<field name="in_warehouse_id" readonly="state != 'draft'" />
|
||||
<field name="in_route_id" readonly="state != 'draft'" />
|
||||
<field
|
||||
name="out_warehouse_id"
|
||||
invisible="1"
|
||||
readonly="state != 'draft'"
|
||||
/>
|
||||
<field name="location_id" readonly="state != 'draft'" />
|
||||
<field
|
||||
name="out_route_id"
|
||||
invisible="1"
|
||||
readonly="state != 'draft'"
|
||||
/>
|
||||
<field
|
||||
name="customer_to_supplier"
|
||||
readonly="state != 'draft'"
|
||||
invisible="type != 'customer'"
|
||||
/>
|
||||
<field name="state" invisible="1" />
|
||||
<field
|
||||
name="supplier_address_id"
|
||||
context="{'show_address': 1}"
|
||||
options="{'always_reload': 1}"
|
||||
required="customer_to_supplier == True"
|
||||
invisible="customer_to_supplier == False"
|
||||
readonly="state != 'draft'"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group name="comments" string="Description" colspan="2">
|
||||
<field
|
||||
name="description"
|
||||
placeholder="This description will be copied to RMA lines."
|
||||
/>
|
||||
<field
|
||||
name="comment"
|
||||
placeholder="Additional information will be printed in RMA group report."
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="RMA Lines" name="lines">
|
||||
<field
|
||||
name="rma_line_ids"
|
||||
context="{'default_rma_id': id,
|
||||
'default_partner_id': partner_id,
|
||||
'hide_title': True}"
|
||||
>
|
||||
<tree
|
||||
name="lines"
|
||||
editable="bottom"
|
||||
default_order="id asc"
|
||||
create="0"
|
||||
>
|
||||
<field name="sequence" widget="handle" />
|
||||
<field name="partner_id" invisible="True" />
|
||||
<field name="product_id" />
|
||||
<field
|
||||
name="lot_id"
|
||||
groups="stock.group_production_lot"
|
||||
domain="[('product_id', '=', product_id)]"
|
||||
context="{'default_product_id': product_id}"
|
||||
/>
|
||||
<field name="name" />
|
||||
<field name="uom_id" groups="uom.group_uom" />
|
||||
<field name="operation_id" />
|
||||
<field name="location_id" invisible="True" />
|
||||
<field name="in_route_id" invisible="True" />
|
||||
<field name="out_route_id" invisible="True" />
|
||||
<field name="in_warehouse_id" invisible="True" />
|
||||
<field name="out_warehouse_id" invisible="True" />
|
||||
<field name="customer_to_supplier" invisible="True" />
|
||||
<field name="supplier_address_id" invisible="True" />
|
||||
<field name="supplier_to_customer" invisible="True" />
|
||||
<field name="customer_address_id" invisible="True" />
|
||||
<field name="receipt_policy" invisible="True" />
|
||||
<field name="delivery_policy" invisible="True" />
|
||||
<field name="delivery_address_id" invisible="True" />
|
||||
<field name="product_qty" />
|
||||
<field name="price_unit" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
</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="inherit_id" ref="rma.view_rma_form" />
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='action_view_lines']" position="attributes">
|
||||
<attribute name="groups">rma.group_rma_supplier_user</attribute>
|
||||
</xpath>
|
||||
<field name="partner_id" position="attributes">
|
||||
<attribute name="string">Supplier</attribute>
|
||||
<attribute
|
||||
name="context"
|
||||
>{'res_partner_search_mode': 'supplier'}</attribute>
|
||||
</field>
|
||||
<group name="inbound_route" position="after">
|
||||
<group name="outbound_route" string="Outbound">
|
||||
<field name="supplier_to_customer" readonly="state != 'draft'" />
|
||||
<field
|
||||
name="customer_address_id"
|
||||
context="{'show_address': 1}"
|
||||
options="{'always_reload': 1}"
|
||||
required="supplier_to_customer == True"
|
||||
invisible="supplier_to_customer == False"
|
||||
readonly="state != 'draft'"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="rma_line_ids" position="attributes">
|
||||
<attribute name="context">{
|
||||
'form_view_ref':'rma.view_rma_line_supplier_form',
|
||||
'default_rma_id': id,
|
||||
'default_partner_id': partner_id,
|
||||
'supplier': 1,
|
||||
'hide_title': True}
|
||||
</attribute>
|
||||
</field>
|
||||
<field name="operation_default_id" position="attributes">
|
||||
<attribute name="domain">[('type','=','supplier')]</attribute>
|
||||
</field>
|
||||
<field name="in_warehouse_id" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</field>
|
||||
<field name="out_warehouse_id" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</field>
|
||||
<field name="in_route_id" position="attributes">
|
||||
<attribute name="invisible">0</attribute>
|
||||
</field>
|
||||
<field name="out_route_id" position="attributes">
|
||||
<attribute name="invisible">0</attribute>
|
||||
</field>
|
||||
</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" />
|
||||
<separator />
|
||||
<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':'date_rma'}"
|
||||
/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_rma_customer" model="ir.actions.act_window">
|
||||
<field name="name">Customer RMA Group</field>
|
||||
<field name="res_model">rma.order</field>
|
||||
<field name="domain">[('type','=', 'customer')]</field>
|
||||
<field name="context">{'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 Group</field>
|
||||
<field name="res_model">rma.order</field>
|
||||
<field name="domain">[('type','=', 'supplier')]</field>
|
||||
<field name="context">{'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>
|
||||
|
||||
</odoo>
|
||||
51
rma/views/stock_view.xml
Normal file
51
rma/views/stock_view.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<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="origin_grp" position="after">
|
||||
<group name="rma">
|
||||
<field name="rma_line_id" />
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_move_tree" model="ir.ui.view">
|
||||
<field name="name">stock.move.tree - rma_line_id</field>
|
||||
<field name="model">stock.move</field>
|
||||
<field name="inherit_id" ref="stock.view_move_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="rma_line_id" optional="hide" invisible="not rma_line_id" />
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_picking_form" model="ir.ui.view">
|
||||
<field name="name">stock.picking.form.inherit</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath
|
||||
expr="//page[@name='operations']/field[@name='move_ids_without_package']/tree"
|
||||
position="inside"
|
||||
>
|
||||
<field name="rma_line_id" optional="hide" invisible="not rma_line_id" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="stock_location_route_form_view_inherit_rma_stock" model="ir.ui.view">
|
||||
<field name="name">stock.route.form</field>
|
||||
<field name="inherit_id" ref="stock.stock_location_route_form_view" />
|
||||
<field name="model">stock.route</field>
|
||||
<field name="arch" type="xml">
|
||||
<label for="warehouse_selectable" position="before">
|
||||
<field name="rma_selectable" string="RMA Order Lines" />
|
||||
</label>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
22
rma/views/stock_warehouse.xml
Normal file
22
rma/views/stock_warehouse.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" ?>
|
||||
<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>
|
||||
<field name="resupply_wh_ids" position="after">
|
||||
<field name="rma_in_this_wh" />
|
||||
</field>
|
||||
<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>
|
||||
4
rma/wizards/__init__.py
Normal file
4
rma/wizards/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from . import rma_add_stock_move
|
||||
from . import rma_make_picking
|
||||
from . import rma_order_line_make_supplier_rma
|
||||
from . import rma_add_serial
|
||||
157
rma/wizards/rma_add_serial.py
Normal file
157
rma/wizards/rma_add_serial.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# Copyright 2022 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class RmaAddSerialWiz(models.TransientModel):
|
||||
_name = "rma.add.serial.wiz"
|
||||
_description = "Wizard to add rma lines from Serial/Lot numbers"
|
||||
|
||||
rma_id = fields.Many2one(
|
||||
comodel_name="rma.order", string="RMA Order", readonly=True, ondelete="cascade"
|
||||
)
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner", string="Partner", readonly=True
|
||||
)
|
||||
partner_shipping_id = fields.Many2one(
|
||||
string="Deliver To",
|
||||
comodel_name="res.partner",
|
||||
)
|
||||
lot_ids = fields.Many2many(
|
||||
comodel_name="stock.lot",
|
||||
string="Lots/Serials selected",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
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
|
||||
return res
|
||||
|
||||
@api.onchange("partner_id")
|
||||
def onchange_partner_id(self):
|
||||
if not self.partner_id:
|
||||
self.update(
|
||||
{
|
||||
"partner_shipping_id": False,
|
||||
}
|
||||
)
|
||||
return
|
||||
|
||||
self = self.with_company(self.rma_id.company_id)
|
||||
addr = self.partner_id.address_get(["delivery"])
|
||||
self.update(
|
||||
{
|
||||
"partner_shipping_id": addr["delivery"],
|
||||
}
|
||||
)
|
||||
|
||||
def _prepare_rma_line_from_lot_vals(self, lot):
|
||||
operation = self.rma_id.operation_default_id
|
||||
if not operation:
|
||||
if self.env.context.get("customer"):
|
||||
operation = (
|
||||
lot.product_id.rma_customer_operation_id
|
||||
or lot.product_id.categ_id.rma_customer_operation_id
|
||||
)
|
||||
else:
|
||||
operation = (
|
||||
lot.product_id.rma_supplier_operation_id
|
||||
or lot.product_id.categ_id.rma_supplier_operation_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.route"].search(
|
||||
[("rma_selectable", "=", True)], limit=1
|
||||
)
|
||||
if not route:
|
||||
raise ValidationError(_("Please define an RMA route"))
|
||||
|
||||
in_warehouse = self.rma_id.in_warehouse_id or operation.in_warehouse_id
|
||||
in_route = self.rma_id.in_route_id or operation.in_route_id
|
||||
out_warehouse = self.rma_id.out_warehouse_id or operation.out_warehouse_id
|
||||
out_route = self.rma_id.out_route_id or operation.out_route_id
|
||||
if not in_warehouse or not out_warehouse:
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[
|
||||
("company_id", "=", self.rma_id.company_id.id),
|
||||
("lot_rma_id", "!=", False),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if not warehouse:
|
||||
raise ValidationError(
|
||||
_("Please define a warehouse with a default RMA location")
|
||||
)
|
||||
in_warehouse = in_warehouse or warehouse
|
||||
out_warehouse = out_warehouse or warehouse
|
||||
location = self.rma_id.location_id
|
||||
if not location:
|
||||
location = (
|
||||
operation.location_id
|
||||
or operation.in_warehouse_id.lot_rma_id
|
||||
or in_warehouse.lot_rma_id
|
||||
or out_warehouse.lot_rma_id
|
||||
)
|
||||
|
||||
product_qty = 1 # serial
|
||||
if lot.product_id.tracking == "lot":
|
||||
# TODO: improve logic, allow to specify qty?
|
||||
product_qty = 1
|
||||
|
||||
vals = {
|
||||
"partner_id": self.partner_id.id,
|
||||
"description": self.rma_id.description,
|
||||
"product_id": lot.product_id.id,
|
||||
"lot_id": lot.id,
|
||||
"uom_id": lot.product_id.uom_id.id,
|
||||
"operation_id": operation.id,
|
||||
"product_qty": product_qty,
|
||||
"delivery_address_id": self.partner_shipping_id.id or self.partner_id.id,
|
||||
"rma_id": self.rma_id.id,
|
||||
"receipt_policy": operation.receipt_policy,
|
||||
"delivery_policy": operation.delivery_policy,
|
||||
"in_warehouse_id": in_warehouse.id,
|
||||
"out_warehouse_id": out_warehouse.id,
|
||||
"in_route_id": in_route.id,
|
||||
"out_route_id": out_route.id,
|
||||
"location_id": location.id,
|
||||
}
|
||||
return vals
|
||||
|
||||
def action_confirm(self):
|
||||
rma_line_obj = self.env["rma.order.line"]
|
||||
existing_lots = self.rma_id.mapped("rma_line_ids.lot_id")
|
||||
for lot in self.lot_ids:
|
||||
if lot in existing_lots:
|
||||
raise ValidationError(
|
||||
_("Lot/Serial Number %s already added.") % lot.name
|
||||
)
|
||||
|
||||
vals = self._prepare_rma_line_from_lot_vals(lot)
|
||||
rec = rma_line_obj.create(vals)
|
||||
# Ensure that configuration on the operation is applied (like
|
||||
# policies).
|
||||
# TODO MIG: in v16 the usage of such onchange can be removed in
|
||||
# favor of (pre)computed stored editable fields for all policies
|
||||
# and configuration in the RMA operation.
|
||||
rec._onchange_operation_id()
|
||||
rec.price_unit = rec._get_price_unit()
|
||||
return {"type": "ir.actions.act_window_close"}
|
||||
102
rma/wizards/rma_add_serial_views.xml
Normal file
102
rma/wizards/rma_add_serial_views.xml
Normal file
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" ?>
|
||||
<odoo>
|
||||
<record id="view_rma_add_serial_wiz_customer" model="ir.ui.view">
|
||||
<field name="name">rma.add.serial.wiz.customer</field>
|
||||
<field name="model">rma.add.serial.wiz</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<group>
|
||||
<field name="partner_id" string="Customer" />
|
||||
<field
|
||||
name="partner_shipping_id"
|
||||
groups="rma.group_rma_delivery_invoice_address"
|
||||
string="Shipping Address"
|
||||
/>
|
||||
</group>
|
||||
<separator string="Select Lot/Serial Numbers to add" />
|
||||
<field name="lot_ids" />
|
||||
<footer>
|
||||
<button
|
||||
string="Confirm"
|
||||
name="action_confirm"
|
||||
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_serial_wiz_customer" model="ir.actions.act_window">
|
||||
<field name="name">Add from Lot/Serial Numbers</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">rma.add.serial.wiz</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="view_id" ref="view_rma_add_serial_wiz_customer" />
|
||||
<field name="groups_id" eval="[(4, ref('rma.group_rma_customer_user'))]" />
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_rma_add_serial_wiz_customer_form" model="ir.ui.view">
|
||||
<field name="name">rma.order.form - rma.add.serial.wiz</field>
|
||||
<field name="model">rma.order</field>
|
||||
<field name="inherit_id" ref="rma.view_rma_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<button
|
||||
name="%(action_rma_add_serial_wiz_customer)d"
|
||||
string="Add Lot/Serial Numbers"
|
||||
type="action"
|
||||
invisible="type != 'customer'"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_rma_add_serial_wiz_supplier" model="ir.ui.view">
|
||||
<field name="name">rma.add.serial.wiz.supplier</field>
|
||||
<field name="model">rma.add.serial.wiz</field>
|
||||
<field name="inherit_id" ref="view_rma_add_serial_wiz_customer" />
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="partner_id" position="attributes">
|
||||
<attribute name="string">Supplier</attribute>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_rma_add_serial_wiz_supplier" model="ir.actions.act_window">
|
||||
<field name="name">Add from Lot/Serial Number</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">rma.add.serial.wiz</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="view_id" ref="view_rma_add_serial_wiz_supplier" />
|
||||
<field name="groups_id" eval="[(4, ref('rma.group_rma_supplier_user'))]" />
|
||||
</record>
|
||||
|
||||
<record id="view_rma_add_serial_wiz_button_supplier_form" model="ir.ui.view">
|
||||
<field name="name">rma.order.supplier.form - rma.add.serial.wiz</field>
|
||||
<field name="model">rma.order</field>
|
||||
<field name="inherit_id" ref="rma.view_rma_supplier_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<button
|
||||
name="%(action_rma_add_serial_wiz_supplier)d"
|
||||
string="Add Lot/Serial numbers"
|
||||
type="action"
|
||||
invisible="type != 'supplier'"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
199
rma/wizards/rma_add_stock_move.py
Normal file
199
rma/wizards/rma_add_stock_move.py
Normal file
@@ -0,0 +1,199 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.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_list):
|
||||
res = super().default_get(fields_list)
|
||||
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["move_ids"] = False
|
||||
return res
|
||||
|
||||
rma_id = fields.Many2one(
|
||||
comodel_name="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(
|
||||
comodel_name="stock.move",
|
||||
string="Stock Moves",
|
||||
domain="[('state', '=', 'done')]",
|
||||
)
|
||||
show_lot_filter = fields.Boolean(
|
||||
string="Show lot filter?",
|
||||
compute="_compute_lot_domain",
|
||||
)
|
||||
lot_domain_ids = fields.Many2many(
|
||||
comodel_name="stock.lot",
|
||||
string="Lots Domain",
|
||||
compute="_compute_lot_domain",
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"move_ids.move_line_ids.lot_id",
|
||||
)
|
||||
def _compute_lot_domain(self):
|
||||
for rec in self:
|
||||
rec.lot_domain_ids = rec.mapped("move_ids.move_line_ids.lot_id").ids
|
||||
rec.show_lot_filter = bool(rec.lot_domain_ids)
|
||||
|
||||
lot_ids = fields.Many2many(comodel_name="stock.lot", string="Lots/Serials selected")
|
||||
|
||||
def select_all(self):
|
||||
self.ensure_one()
|
||||
self.write(
|
||||
{
|
||||
"lot_ids": [(6, 0, self.lot_domain_ids.ids)],
|
||||
}
|
||||
)
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"name": _("Add from Stock Move"),
|
||||
"view_mode": "form",
|
||||
"res_model": self._name,
|
||||
"res_id": self.id,
|
||||
"target": "new",
|
||||
}
|
||||
|
||||
def _prepare_rma_line_from_stock_move(self, sm, lot=False):
|
||||
operation = self.rma_id.operation_default_id
|
||||
if not operation:
|
||||
if self.env.context.get("customer"):
|
||||
operation = (
|
||||
sm.product_id.rma_customer_operation_id
|
||||
or sm.product_id.categ_id.rma_customer_operation_id
|
||||
)
|
||||
else:
|
||||
operation = (
|
||||
sm.product_id.rma_supplier_operation_id
|
||||
or sm.product_id.categ_id.rma_supplier_operation_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.route"].search(
|
||||
[("rma_selectable", "=", True)], limit=1
|
||||
)
|
||||
if not route:
|
||||
raise ValidationError(_("Please define an RMA route"))
|
||||
in_warehouse = self.rma_id.in_warehouse_id or operation.in_warehouse_id
|
||||
in_route = self.rma_id.in_route_id or operation.in_route_id
|
||||
out_warehouse = self.rma_id.out_warehouse_id or operation.out_warehouse_id
|
||||
out_route = self.rma_id.out_route_id or operation.out_route_id
|
||||
if not in_warehouse or not out_warehouse:
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[
|
||||
("company_id", "=", self.rma_id.company_id.id),
|
||||
("lot_rma_id", "!=", False),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if not warehouse:
|
||||
raise ValidationError(
|
||||
_("Please define a warehouse with a default RMA location")
|
||||
)
|
||||
in_warehouse = in_warehouse or warehouse
|
||||
out_warehouse = out_warehouse or warehouse
|
||||
location = self.rma_id.location_id
|
||||
if not location:
|
||||
location = (
|
||||
operation.location_id
|
||||
or operation.in_warehouse_id.lot_rma_id
|
||||
or in_warehouse.lot_rma_id
|
||||
or out_warehouse.lot_rma_id
|
||||
)
|
||||
product_qty = sm.product_uom_qty
|
||||
if sm.product_id.tracking == "serial":
|
||||
product_qty = 1
|
||||
elif sm.product_id.tracking == "lot":
|
||||
product_qty = sum(
|
||||
sm.move_line_ids.filtered(lambda x: x.lot_id.id == lot.id).mapped(
|
||||
"quantity"
|
||||
)
|
||||
)
|
||||
data = {
|
||||
"partner_id": self.partner_id.id,
|
||||
"description": self.rma_id.description,
|
||||
"reference_move_id": sm.id,
|
||||
"product_id": sm.product_id.id,
|
||||
"lot_id": lot and lot.id or False,
|
||||
"origin": sm.picking_id.name or sm.name,
|
||||
"uom_id": sm.product_uom.id,
|
||||
"operation_id": operation.id,
|
||||
"product_qty": product_qty,
|
||||
"delivery_address_id": sm.picking_id.partner_id.id,
|
||||
"rma_id": self.rma_id.id,
|
||||
"receipt_policy": operation.receipt_policy,
|
||||
"delivery_policy": operation.delivery_policy,
|
||||
"in_warehouse_id": in_warehouse.id,
|
||||
"out_warehouse_id": out_warehouse.id,
|
||||
"in_route_id": in_route.id,
|
||||
"out_route_id": out_route.id,
|
||||
"location_id": location.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
|
||||
|
||||
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:
|
||||
tracking_move = sm.product_id.tracking in ("serial", "lot")
|
||||
if sm not in existing_stock_moves or tracking_move:
|
||||
if sm.product_id.tracking == "none":
|
||||
data = self._prepare_rma_line_from_stock_move(sm, lot=False)
|
||||
rec = rma_line_obj.with_context(
|
||||
default_rma_id=self.rma_id.id
|
||||
).create(data)
|
||||
# Ensure that configuration on the operation is applied (like
|
||||
# policies).
|
||||
# TODO MIG: in v16 the usage of such onchange can be removed in
|
||||
# favor of (pre)computed stored editable fields for all policies
|
||||
# and configuration in the RMA operation.
|
||||
rec._onchange_operation_id()
|
||||
else:
|
||||
for lot in sm.move_line_ids.mapped("lot_id").filtered(
|
||||
lambda x: x.id in self.lot_ids.ids
|
||||
):
|
||||
if lot.id in self.rma_id.rma_line_ids.mapped("lot_id").ids:
|
||||
continue
|
||||
data = self._prepare_rma_line_from_stock_move(sm, lot)
|
||||
rec = rma_line_obj.with_context(
|
||||
default_rma_id=self.rma_id.id
|
||||
).create(data)
|
||||
# Ensure that configuration on the operation is applied (like
|
||||
# policies).
|
||||
# TODO MIG: in v16 the usage of such onchange can be removed in
|
||||
# favor of (pre)computed stored editable fields for all
|
||||
# policies and configuration in the RMA operation.
|
||||
rec._onchange_operation_id()
|
||||
rec.price_unit = rec._get_price_unit()
|
||||
return {"type": "ir.actions.act_window_close"}
|
||||
188
rma/wizards/rma_add_stock_move_view.xml
Normal file
188
rma/wizards/rma_add_stock_move_view.xml
Normal file
@@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" ?>
|
||||
<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" string="Customer" />
|
||||
</group>
|
||||
<separator string="Select Stock Moves to add" />
|
||||
<field name="move_ids">
|
||||
<tree>
|
||||
<field name="product_id" />
|
||||
<field name="product_uom_qty" />
|
||||
<field
|
||||
name="product_uom"
|
||||
string="Unit of Measure"
|
||||
groups="uom.group_uom"
|
||||
/>
|
||||
<field name="picking_id" />
|
||||
<field
|
||||
name="location_id"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field
|
||||
name="location_dest_id"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
/>
|
||||
<field name="create_date" groups="base.group_no_one" />
|
||||
<field name="date" string="Date" groups="base.group_no_one" />
|
||||
<field name="state" />
|
||||
</tree>
|
||||
</field>
|
||||
<field name="show_lot_filter" invisible="1" />
|
||||
<field name="lot_domain_ids" widget="many2many_tags" invisible="1" />
|
||||
<div class="oe_grey" invisible="show_lot_filter == False">
|
||||
The creation of the RMA Lines will be separated according to the lots or serials selected
|
||||
</div>
|
||||
<div class="o_row">
|
||||
<label
|
||||
for="lot_ids"
|
||||
invisible="show_lot_filter == False"
|
||||
string="Selected Lot/Serial Numbers"
|
||||
/>
|
||||
<field
|
||||
name="lot_ids"
|
||||
widget="many2many_tags"
|
||||
domain="[('id', 'in', lot_domain_ids)]"
|
||||
invisible="show_lot_filter == False"
|
||||
options="{'no_create': True}"
|
||||
/>
|
||||
<button
|
||||
name="select_all"
|
||||
type="object"
|
||||
string="Select all"
|
||||
class="oe_inline"
|
||||
invisible="show_lot_filter == False"
|
||||
/>
|
||||
</div>
|
||||
<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="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.form - stock.move wizard</field>
|
||||
<field name="model">rma.order</field>
|
||||
<field name="inherit_id" ref="rma.view_rma_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<button
|
||||
name="%(action_rma_add_stock_move_customer)d"
|
||||
string="Add From Stock Move"
|
||||
type="action"
|
||||
invisible="type != 'customer'"
|
||||
/>
|
||||
</xpath>
|
||||
</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" string="Supplier" />
|
||||
</group>
|
||||
<separator string="Select Stock Moves to add" />
|
||||
<field
|
||||
name="move_ids"
|
||||
domain="[('picking_id.partner_id', '=', partner_id), ('location_id.usage', '=', 'supplier')]"
|
||||
/>
|
||||
<field name="show_lot_filter" invisible="1" />
|
||||
<field name="lot_domain_ids" widget="many2many_tags" invisible="1" />
|
||||
<div class="o_row">
|
||||
<label
|
||||
for="lot_ids"
|
||||
invisible="show_lot_filter == False"
|
||||
string="Selected Lot/Serial Numbers"
|
||||
/>
|
||||
<field
|
||||
name="lot_ids"
|
||||
widget="many2many_tags"
|
||||
domain="[('id', 'in', lot_domain_ids)]"
|
||||
invisible="show_lot_filter == False"
|
||||
options="{'no_create': True}"
|
||||
/>
|
||||
<button
|
||||
name="select_all"
|
||||
type="object"
|
||||
string="Select all"
|
||||
class="oe_inline"
|
||||
invisible="show_lot_filter == False"
|
||||
/>
|
||||
</div>
|
||||
<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="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.supplier.form - stock.move wizard</field>
|
||||
<field name="model">rma.order</field>
|
||||
<field name="inherit_id" ref="rma.view_rma_supplier_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//header" position="inside">
|
||||
<button
|
||||
name="%(action_rma_add_stock_move_supplier)d"
|
||||
string="Add From Stock Move"
|
||||
type="action"
|
||||
invisible="type != 'supplier'"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
288
rma/wizards/rma_make_picking.py
Normal file
288
rma/wizards/rma_make_picking.py
Normal file
@@ -0,0 +1,288 @@
|
||||
# Copyright (C) 2017-20 ForgeFlow S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
import time
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DT_FORMAT
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
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 and line.rma_id.id or False,
|
||||
}
|
||||
return values
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
"""Default values for wizard, if there is more than one supplier on
|
||||
lines the supplier field is empty otherwise is the unique line
|
||||
supplier.
|
||||
"""
|
||||
context = self._context.copy()
|
||||
res = super().default_get(fields_list)
|
||||
rma_line_obj = self.env["rma.order.line"]
|
||||
rma_line_ids = self.env.context["active_ids"] or []
|
||||
active_model = self.env.context["active_model"]
|
||||
|
||||
if not rma_line_ids:
|
||||
return res
|
||||
assert active_model == "rma.order.line", "Bad context propagation"
|
||||
|
||||
items = []
|
||||
lines = rma_line_obj.browse(rma_line_ids)
|
||||
if len(lines.mapped("partner_id")) > 1:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Only RMA lines from the same partner can be processed at "
|
||||
"the same time"
|
||||
)
|
||||
)
|
||||
for line in lines:
|
||||
items.append([0, 0, self._prepare_item(line)])
|
||||
res["item_ids"] = items
|
||||
context.update({"items_ids": items})
|
||||
return res
|
||||
|
||||
item_ids = fields.One2many("rma_make_picking.wizard.item", "wiz_id", string="Items")
|
||||
|
||||
def find_procurement_group(self, item):
|
||||
if item.line_id.rma_id:
|
||||
return self.env["procurement.group"].search(
|
||||
[("rma_id", "=", item.line_id.rma_id.id)]
|
||||
)
|
||||
else:
|
||||
return self.env["procurement.group"].search(
|
||||
[("rma_line_id", "=", item.line_id.id)]
|
||||
)
|
||||
|
||||
def _get_procurement_group_data(self, item):
|
||||
group_data = {
|
||||
"partner_id": item.line_id.partner_id.id,
|
||||
"name": item.line_id.rma_id.name or item.line_id.name,
|
||||
"rma_id": item.line_id.rma_id and item.line_id.rma_id.id or False,
|
||||
"rma_line_id": item.line_id.id if not item.line_id.rma_id else False,
|
||||
}
|
||||
return group_data
|
||||
|
||||
@api.model
|
||||
def _get_address(self, item):
|
||||
if item.line_id.customer_to_supplier:
|
||||
delivery_address = item.line_id.supplier_address_id
|
||||
elif item.line_id.supplier_to_customer:
|
||||
delivery_address = item.line_id.customer_address_id
|
||||
elif item.line_id.delivery_address_id:
|
||||
delivery_address = item.line_id.delivery_address_id
|
||||
elif item.line_id.partner_id:
|
||||
delivery_address = item.line_id.partner_id
|
||||
else:
|
||||
raise ValidationError(_("Unknown delivery address"))
|
||||
return delivery_address
|
||||
|
||||
@api.model
|
||||
def _get_address_location(self, delivery_address_id, a_type):
|
||||
if a_type == "supplier":
|
||||
return delivery_address_id.property_stock_supplier
|
||||
elif a_type == "customer":
|
||||
return delivery_address_id.property_stock_customer
|
||||
|
||||
@api.model
|
||||
def _get_procurement_data(self, item, group, qty, picking_type):
|
||||
line = item.line_id
|
||||
delivery_address_id = self._get_address(item)
|
||||
location, warehouse, route = False, False, False
|
||||
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
|
||||
elif picking_type == "outgoing":
|
||||
if line.supplier_to_customer:
|
||||
location = self._get_address_location(delivery_address_id, "customer")
|
||||
else:
|
||||
location = self._get_address_location(delivery_address_id, line.type)
|
||||
warehouse = line.out_warehouse_id
|
||||
route = line.out_route_id
|
||||
if not route:
|
||||
raise ValidationError(_("No route specified"))
|
||||
if not warehouse:
|
||||
raise ValidationError(_("No warehouse specified"))
|
||||
procurement_data = {
|
||||
"name": line.rma_id and line.rma_id.name or line.name,
|
||||
"group_id": group,
|
||||
"origin": group and group.name or line.name,
|
||||
"warehouse_id": warehouse,
|
||||
"date_planned": time.strftime(DT_FORMAT),
|
||||
"product_id": item.product_id,
|
||||
"product_qty": qty,
|
||||
"partner_id": delivery_address_id.id,
|
||||
"product_uom": line.product_id.product_tmpl_id.uom_id.id,
|
||||
"location_id": location,
|
||||
"rma_line_id": line.id,
|
||||
"route_ids": route,
|
||||
}
|
||||
return procurement_data
|
||||
|
||||
@api.model
|
||||
def _create_procurement(self, item, picking_type):
|
||||
errors = []
|
||||
group = self.find_procurement_group(item)
|
||||
if not group:
|
||||
pg_data = self._get_procurement_group_data(item)
|
||||
group = self.env["procurement.group"].create(pg_data)
|
||||
if picking_type == "incoming":
|
||||
qty = item.qty_to_receive
|
||||
else:
|
||||
qty = item.qty_to_deliver
|
||||
values = self._get_procurement_data(item, group, qty, picking_type)
|
||||
product = item.line_id.product_id
|
||||
if float_compare(qty, 0, product.uom_id.rounding) != 1:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"No quantity to transfer on %(arg1)s shipment of product %(arg2)s.",
|
||||
arg1=_(picking_type),
|
||||
arg2=product.default_code or product.name,
|
||||
)
|
||||
)
|
||||
# create picking
|
||||
procurements = []
|
||||
try:
|
||||
procurement = group.Procurement(
|
||||
item.line_id.product_id,
|
||||
qty,
|
||||
item.line_id.product_id.product_tmpl_id.uom_id,
|
||||
values.get("location_id"),
|
||||
values.get("origin"),
|
||||
values.get("origin"),
|
||||
self.env.company,
|
||||
values,
|
||||
)
|
||||
|
||||
procurements.append(procurement)
|
||||
# Trigger a route check with a mutable in the context that can be
|
||||
# cleared after the first rule selection
|
||||
self.env["procurement.group"].with_context(rma_route_check=[True]).run(
|
||||
procurements
|
||||
)
|
||||
except UserError as error:
|
||||
errors.append(error.args[0])
|
||||
if errors:
|
||||
raise UserError("\n".join(errors))
|
||||
return procurements
|
||||
|
||||
def _create_picking(self):
|
||||
"""Method called when the user clicks on create picking"""
|
||||
picking_type = self.env.context.get("picking_type")
|
||||
procurements = []
|
||||
for item in self.item_ids:
|
||||
line = item.line_id
|
||||
if line.state != "approved":
|
||||
raise ValidationError(_("RMA %s is not approved") % line.name)
|
||||
if line.receipt_policy == "no" and picking_type == "incoming":
|
||||
raise ValidationError(_("No shipments needed for this operation"))
|
||||
if line.delivery_policy == "no" and picking_type == "outgoing":
|
||||
raise ValidationError(_("No deliveries needed for this operation"))
|
||||
procurement = self._create_procurement(item, picking_type)
|
||||
procurements.extend(procurement)
|
||||
return procurements
|
||||
|
||||
def action_create_picking(self):
|
||||
self._create_picking()
|
||||
move_line_model = self.env["stock.move.line"]
|
||||
picking_type = self.env.context.get("picking_type")
|
||||
if picking_type == "outgoing":
|
||||
pickings = self.mapped("item_ids.line_id")._get_out_pickings()
|
||||
action = self.item_ids.line_id.action_view_out_shipments()
|
||||
else:
|
||||
pickings = self.mapped("item_ids.line_id")._get_in_pickings()
|
||||
action = self.item_ids.line_id.action_view_in_shipments()
|
||||
# Force the reservation of the RMA specific lot for incoming shipments.
|
||||
# FIXME: still needs fixing, not reserving appropriate serials.
|
||||
for move in pickings.move_ids.filtered(
|
||||
lambda x: x.state not in ("draft", "cancel", "done", "waiting")
|
||||
and x.rma_line_id
|
||||
and x.product_id.tracking in ("lot", "serial")
|
||||
and x.rma_line_id.lot_id
|
||||
):
|
||||
# Force the reservation of the RMA specific lot for incoming shipments.
|
||||
move.move_line_ids.unlink()
|
||||
if move.product_id.tracking == "serial":
|
||||
move.write(
|
||||
{
|
||||
"lot_ids": [(6, 0, move.rma_line_id.lot_id.ids)],
|
||||
}
|
||||
)
|
||||
quants = self.env["stock.quant"]._gather(
|
||||
move.product_id, move.location_id, lot_id=move.rma_line_id.lot_id
|
||||
)
|
||||
move.move_line_ids.write(
|
||||
{
|
||||
"package_id": len(quants) == 1 and quants.package_id.id,
|
||||
}
|
||||
)
|
||||
elif move.product_id.tracking == "lot":
|
||||
if picking_type == "incoming":
|
||||
qty = self.item_ids.filtered(
|
||||
lambda x, move=move: x.line_id.id == move.rma_line_id.id
|
||||
).qty_to_receive
|
||||
|
||||
else:
|
||||
qty = self.item_ids.filtered(
|
||||
lambda x, move=move: x.line_id.id == move.rma_line_id.id
|
||||
).qty_to_deliver
|
||||
move_line_data = move._prepare_move_line_vals()
|
||||
move_line_data.update(
|
||||
{
|
||||
"lot_id": move.rma_line_id.lot_id.id,
|
||||
"product_uom_id": move.product_id.uom_id.id,
|
||||
"quantity": qty,
|
||||
}
|
||||
)
|
||||
move_line_model.create(move_line_data)
|
||||
pickings.with_context(force_no_bypass_reservation=True).action_assign()
|
||||
return action
|
||||
|
||||
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, ondelete="cascade"
|
||||
)
|
||||
line_id = fields.Many2one(
|
||||
"rma.order.line", string="RMA order Line", ondelete="cascade"
|
||||
)
|
||||
rma_id = fields.Many2one("rma.order", related="line_id.rma_id", string="RMA Group")
|
||||
product_id = fields.Many2one("product.product", string="Product")
|
||||
product_qty = fields.Float(
|
||||
related="line_id.product_qty",
|
||||
string="Quantity Ordered",
|
||||
copy=False,
|
||||
digits="Product Unit of Measure",
|
||||
)
|
||||
qty_to_receive = fields.Float(
|
||||
string="Quantity to Receive", digits="Product Unit of Measure"
|
||||
)
|
||||
qty_to_deliver = fields.Float(
|
||||
string="Quantity To Deliver", digits="Product Unit of Measure"
|
||||
)
|
||||
uom_id = fields.Many2one("uom.uom", string="Unit of Measure")
|
||||
137
rma/wizards/rma_make_picking_view.xml
Normal file
137
rma/wizards/rma_make_picking_view.xml
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" ?>
|
||||
<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 name="RMA Lines" editable="bottom" create="false">
|
||||
<field
|
||||
name="rma_id"
|
||||
groups="rma.group_rma_groups"
|
||||
readonly="1"
|
||||
/>
|
||||
<field name="product_id" readonly="1" />
|
||||
<field name="product_qty" />
|
||||
<field name="line_id" invisible="1" />
|
||||
<field name="uom_id" groups="uom.group_uom" readonly="1" />
|
||||
<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 name="RMA Lines" editable="bottom" create="false">
|
||||
<field name="rma_id" groups="rma.group_rma_groups" />
|
||||
<field name="product_id" />
|
||||
<field name="product_qty" />
|
||||
<field name="line_id" invisible="1" />
|
||||
<field name="uom_id" groups="uom.group_uom" />
|
||||
<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="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>
|
||||
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||
</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="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>
|
||||
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||
</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"
|
||||
string="Create Incoming Shipment"
|
||||
class="oe_highlight"
|
||||
invisible="qty_to_receive == 0 or qty_to_receive < 0 or state != 'approved' or receipt_policy == 'no'"
|
||||
type="action"
|
||||
/>
|
||||
<button
|
||||
name="%(action_rma_picking_in)d"
|
||||
string="Create Incoming Shipment"
|
||||
invisible="qty_to_receive > 0 or state != 'approved' or receipt_policy == 'no'"
|
||||
type="action"
|
||||
/>
|
||||
<button
|
||||
name="%(action_rma_picking_out)d"
|
||||
string="Create Delivery"
|
||||
class="oe_highlight"
|
||||
invisible="qty_to_deliver == 0 or qty_to_deliver < 0 or state != 'approved' or delivery_policy == 'no'"
|
||||
type="action"
|
||||
/>
|
||||
<button
|
||||
name="%(action_rma_picking_out)d"
|
||||
string="Create Delivery"
|
||||
invisible="qty_to_deliver > 0 or state != 'approved' or delivery_policy == 'no'"
|
||||
type="action"
|
||||
/>
|
||||
</header>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
202
rma/wizards/rma_order_line_make_supplier_rma.py
Normal file
202
rma/wizards/rma_order_line_make_supplier_rma.py
Normal file
@@ -0,0 +1,202 @@
|
||||
# Copyright 2017 ForgeFlow
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class RmaLineMakeSupplierRma(models.TransientModel):
|
||||
_name = "rma.order.line.make.supplier.rma"
|
||||
_description = "RMA Line Make Supplier RMA"
|
||||
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Supplier",
|
||||
required=True,
|
||||
)
|
||||
item_ids = fields.One2many(
|
||||
comodel_name="rma.order.line.make.supplier.rma.item",
|
||||
inverse_name="wiz_id",
|
||||
string="Items",
|
||||
)
|
||||
supplier_rma_id = fields.Many2one(
|
||||
comodel_name="rma.order", string="Supplier RMA Order Group"
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _get_default_operation(self):
|
||||
"""Dropshipping is the most common use case of this wizard, thus
|
||||
trying to default to a dropshipping operation first."""
|
||||
operation = self.env["rma.operation"].search(
|
||||
[("type", "=", "supplier"), ("supplier_to_customer", "=", True)], limit=1
|
||||
)
|
||||
if not operation:
|
||||
operation = self.env["rma.operation"].search(
|
||||
[("type", "=", "supplier")], limit=1
|
||||
)
|
||||
return operation
|
||||
|
||||
@api.model
|
||||
def _prepare_item(self, line):
|
||||
operation = self._get_default_operation()
|
||||
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,
|
||||
"operation_id": operation.id if operation else False,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
rma_line_obj = self.env["rma.order.line"]
|
||||
rma_line_ids = self.env.context["active_ids"] or []
|
||||
active_model = self.env.context["active_model"]
|
||||
|
||||
if not rma_line_ids:
|
||||
return res
|
||||
assert active_model == "rma.order.line", "Bad context propagation"
|
||||
|
||||
items = []
|
||||
lines = rma_line_obj.browse(rma_line_ids)
|
||||
for line in lines:
|
||||
items.append([0, 0, self._prepare_item(line)])
|
||||
suppliers = lines.mapped(
|
||||
lambda r: r.supplier_address_id.parent_id or r.supplier_address_id
|
||||
)
|
||||
if len(suppliers) > 1:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"Only RMA lines from the same supplier can be "
|
||||
"processed at the same time"
|
||||
)
|
||||
)
|
||||
res["partner_id"] = suppliers.id
|
||||
res["item_ids"] = items
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _prepare_supplier_rma(self, company):
|
||||
if not self.partner_id:
|
||||
raise ValidationError(_("Enter a supplier."))
|
||||
return {
|
||||
"partner_id": self.partner_id.id,
|
||||
"type": "supplier",
|
||||
"company_id": company.id,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _prepare_supplier_rma_line(self, rma, item):
|
||||
if item.operation_id:
|
||||
operation = item.operation_id
|
||||
else:
|
||||
operation = self._get_default_operation()
|
||||
if not operation.in_route_id or not operation.out_route_id:
|
||||
route = self.env["stock.route"].search(
|
||||
[("rma_selectable", "=", True)], limit=1
|
||||
)
|
||||
if not route:
|
||||
raise ValidationError(_("Please define an RMA route"))
|
||||
if not operation.in_warehouse_id or not operation.out_warehouse_id:
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[
|
||||
("company_id", "=", self.rma_id.company_id.id),
|
||||
("lot_rma_id", "!=", False),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
if not warehouse:
|
||||
raise ValidationError(
|
||||
_("Please define a warehouse with a default RMA location")
|
||||
)
|
||||
data = {
|
||||
"partner_id": self.partner_id.id,
|
||||
"description": self.supplier_rma_id.description,
|
||||
"type": "supplier",
|
||||
"origin": item.line_id.rma_id.name,
|
||||
"customer_address_id": item.line_id.delivery_address_id.id
|
||||
or item.line_id.partner_id.id,
|
||||
"delivery_address_id": self.partner_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 if rma else False,
|
||||
"uom_id": item.line_id.uom_id.id,
|
||||
"operation_id": operation.id,
|
||||
"receipt_policy": operation.receipt_policy,
|
||||
"delivery_policy": operation.delivery_policy,
|
||||
"supplier_to_customer": operation.supplier_to_customer,
|
||||
"in_warehouse_id": operation.in_warehouse_id.id or warehouse.id,
|
||||
"out_warehouse_id": operation.out_warehouse_id.id or warehouse.id,
|
||||
"in_route_id": operation.in_route_id.id or route.id,
|
||||
"out_route_id": operation.out_route_id.id or route.id,
|
||||
"location_id": (
|
||||
operation.location_id.id
|
||||
or operation.in_warehouse_id.lot_rma_id.id
|
||||
or warehouse.lot_rma_id.id
|
||||
),
|
||||
}
|
||||
return data
|
||||
|
||||
def make_supplier_rma(self):
|
||||
self = self.with_context(supplier=True, customer=False)
|
||||
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 ValidationError(_("Enter a positive quantity."))
|
||||
|
||||
if self.supplier_rma_id:
|
||||
rma = self.supplier_rma_id
|
||||
if not rma and len(self.item_ids) > 1:
|
||||
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)
|
||||
rec = rma_line_obj.create(rma_line_data)
|
||||
# Ensure that configuration on the operation is applied (like
|
||||
# policies).
|
||||
# TODO MIG: in v16 the usage of such onchange can be removed in
|
||||
# favor of (pre)computed stored editable fields for all policies
|
||||
# and configuration in the RMA operation.
|
||||
rec._onchange_operation_id()
|
||||
action = self.env.ref("rma.action_rma_supplier_lines")
|
||||
rma_lines = self.item_ids.mapped("line_id.supplier_rma_line_ids").ids
|
||||
result = action.sudo().read()[0]
|
||||
result["domain"] = [("id", "in", rma_lines)]
|
||||
return result
|
||||
|
||||
|
||||
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, ondelete="cascade"
|
||||
)
|
||||
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", readonly=True
|
||||
)
|
||||
name = fields.Char(related="line_id.name", readonly=True)
|
||||
uom_id = fields.Many2one("uom.uom", string="UoM", readonly=True)
|
||||
product_qty = fields.Float(string="Quantity", digits="Product UoS")
|
||||
operation_id = fields.Many2one(
|
||||
comodel_name="rma.operation",
|
||||
string="Operation",
|
||||
domain=[("type", "=", "supplier")],
|
||||
)
|
||||
77
rma/wizards/rma_order_line_make_supplier_rma_view.xml
Normal file
77
rma/wizards/rma_order_line_make_supplier_rma_view.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!-- Copyright 2016 ForgeFlow
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl-3.0) -->
|
||||
<odoo>
|
||||
<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"
|
||||
domain="[('partner_id', '=', partner_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 name="Details" editable="bottom" create="false">
|
||||
<field name="line_id" options="{'no_open': true}" />
|
||||
<field name="product_id" />
|
||||
<field name="name" />
|
||||
<field name="operation_id" />
|
||||
<field name="product_qty" />
|
||||
<field name="uom_id" groups="uom.group_uom" />
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<newline />
|
||||
<footer 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" />
|
||||
</footer>
|
||||
</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_mode">form</field>
|
||||
<field name="view_id" ref="view_rma_order_line_make_supplier_rma" />
|
||||
<field name="target">new</field>
|
||||
<field name="binding_model_id" ref="rma.model_rma_order_line" />
|
||||
</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"
|
||||
string="Create Supplier RMA"
|
||||
invisible="type != 'customer' or state != 'approved'"
|
||||
type="action"
|
||||
/>
|
||||
</header>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user