Skip to content

Commit

Permalink
Merge pull request #513 from ForgeFlow/17_nc_mig_rma_account
Browse files Browse the repository at this point in the history
[17.0] [MIG] rma_account
  • Loading branch information
AaronHForgeFlow authored Aug 22, 2024
2 parents 82f9022 + d0b9d38 commit 62a51f1
Show file tree
Hide file tree
Showing 30 changed files with 2,901 additions and 0 deletions.
52 changes: 52 additions & 0 deletions rma_account/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg
:alt: License LGPL-3

===========
RMA Account
===========

This module integrates Return Merchandise Authorizations (RMA) with invoices,
allowing to:

#. Create complete RMA's using existing invoices as a reference.
#. Create refunds from a RMA.

Usage
=====

RMA are accessible though Inventory menu. There's four menus, divided by type.
Users can access to the list of RMA or RMA lines.

Create an RMA:

#. Select a partner. Fill the rma lines by selecting an invoice.
#. Request approval and approve.
#. Click on RMA Lines button.
#. Click on more and select an option: "Receive products", "Create Delivery
Order, Create Refund".
#. Go back to the RMA. Set the RMA to done if not further action is required.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/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 smashing it by providing a detailed and welcomed feedback.

Credits
=======

Contributors
------------

* Jordi Ballester Alomar <[email protected]>
* Aaron Henriquez <[email protected]>
* Lois Rilo <[email protected]>
* Bhavesh Odedra <[email protected]>
* Akim Juillerat <[email protected]>

Maintainer
----------

This module is maintained by ForgeFlow.
5 changes: 5 additions & 0 deletions rma_account/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2017 ForgeFlow S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)

from . import models
from . import wizards
25 changes: 25 additions & 0 deletions rma_account/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2017 ForgeFlow S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)

{
"name": "RMA Account",
"version": "17.0.1.0.0",
"license": "LGPL-3",
"category": "RMA",
"summary": "Integrates RMA with Invoice Processing",
"author": "ForgeFlow",
"website": "https://github.com/ForgeFlow",
"depends": ["stock_account", "rma"],
"data": [
"security/ir.model.access.csv",
"data/rma_operation.xml",
"views/rma_order_view.xml",
"views/rma_operation_view.xml",
"views/rma_order_line_view.xml",
"views/account_move_view.xml",
"views/rma_account_menu.xml",
"wizards/rma_add_account_move.xml",
"wizards/rma_refund.xml",
],
"installable": True,
}
42 changes: 42 additions & 0 deletions rma_account/data/rma_operation.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" ?>
<odoo noupdate="1">

<record id="rma.rma_operation_customer_replace" model="rma.operation">
<field name="refund_policy">no</field>
</record>

<record id="rma.rma_operation_supplier_replace" model="rma.operation">
<field name="refund_policy">no</field>
</record>

<record id="rma_operation_customer_refund" model="rma.operation">
<field name="name">Refund after receive</field>
<field name="code">RF-C</field>
<field name="refund_policy">received</field>
<field name="receipt_policy">ordered</field>
<field name="delivery_policy">no</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_refund" model="rma.operation">
<field name="name">Refund after deliver</field>
<field name="code">RF-S</field>
<field name="refund_policy">ordered</field>
<field name="receipt_policy">no</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.rma_operation_ds_replace" model="rma.operation">
<field name="refund_policy">no</field>
</record>

<record id="rma.rma_operation_ds_replace_supplier" model="rma.operation">
<field name="refund_policy">no</field>
</record>

</odoo>
8 changes: 8 additions & 0 deletions rma_account/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from . import rma_order
from . import rma_order_line
from . import rma_operation
from . import account_move
from . import account_move_line
from . import procurement
from . import stock_move
from . import stock_valuation_layer
147 changes: 147 additions & 0 deletions rma_account/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Copyright 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.tools.float_utils import float_compare


class AccountMove(models.Model):
_inherit = "account.move"

@api.depends("line_ids.rma_line_ids")
def _compute_used_in_rma_count(self):
for inv in self:
rmas = inv.mapped("line_ids.rma_line_ids")
inv.used_in_rma_count = len(rmas)

@api.depends("line_ids.rma_line_id")
def _compute_rma_count(self):
for inv in self:
rmas = inv.mapped("line_ids.rma_line_id")
inv.rma_count = len(rmas)

def _prepare_invoice_line_from_rma_line(self, rma_line):
sequence = max(self.line_ids.mapped("sequence")) + 1 if self.line_ids else 10
qty = rma_line.qty_to_refund
if float_compare(qty, 0.0, precision_rounding=rma_line.uom_id.rounding) <= 0:
qty = 0.0
data = {
"move_id": self.id,
"product_uom_id": rma_line.uom_id.id,
"product_id": rma_line.product_id.id,
"price_unit": rma_line.company_id.currency_id._convert(
rma_line._get_price_unit(),
self.currency_id,
self.company_id,
self.date,
round=False,
),
"quantity": qty,
"discount": 0.0,
"rma_line_id": rma_line.id,
"sequence": sequence + 1,
}
return data

def _post_process_invoice_line_from_rma_line(self, new_line, rma_line):
new_line.rma_line_id = rma_line
new_line.name = f"{self.add_rma_line_id.name}: {new_line.name}"
new_line.account_id = new_line.account_id
return True

@api.onchange("add_rma_line_id")
def onchange_add_rma_line_id(self):
if not self.add_rma_line_id:
return {}
if not self.partner_id:
self.partner_id = self.add_rma_line_id.partner_id.id

new_line = self.env["account.move.line"]
if self.add_rma_line_id not in (self.line_ids.mapped("rma_line_id")):
data = self._prepare_invoice_line_from_rma_line(self.add_rma_line_id)
new_line = new_line.new(data)
self._post_process_invoice_line_from_rma_line(
new_line, self.add_rma_line_id
)
# Compute invoice_origin.
origins = set(self.line_ids.mapped("rma_line_id.name"))
self.invoice_origin = ",".join(list(origins))
self.add_rma_line_id = False

rma_count = fields.Integer(compute="_compute_rma_count", string="# of RMA")
used_in_rma_count = fields.Integer(
compute="_compute_used_in_rma_count", string="# of Used in RMA"
)

add_rma_line_id = fields.Many2one(
comodel_name="rma.order.line",
string="Add from RMA line",
ondelete="set null",
help="Create a refund in based on an existing rma_line",
)

def action_view_used_in_rma(self):
rmas = self.mapped("line_ids.rma_line_ids")
return self._prepare_action_view_rma(rmas)

def action_view_rma(self):
rmas = self.mapped("line_ids.rma_line_id")
return self._prepare_action_view_rma(rmas)

def _prepare_action_view_rma(self, rmas):
if self.move_type in ["in_invoice", "in_refund"]:
action = self.env.ref("rma.action_rma_supplier_lines")
form_view = self.env.ref("rma.view_rma_line_supplier_form", False)
else:
action = self.env.ref("rma.action_rma_customer_lines")
form_view = self.env.ref("rma.view_rma_line_form", False)
result = action.sudo().read()[0]
rma_ids = rmas.ids
# choose the view_mode accordingly
if not rma_ids:
result["domain"] = [("id", "in", [])]
elif len(rma_ids) > 1:
result["domain"] = [("id", "in", rma_ids)]
else:
res = form_view
result["views"] = [(res and res.id or False, "form")]
result["res_id"] = rma_ids and rma_ids[0] or False
return result

def _stock_account_prepare_anglo_saxon_out_lines_vals(self):
product_model = self.env["product.product"]
res = super()._stock_account_prepare_anglo_saxon_out_lines_vals()
for line in res:
if line.get("product_id", False):
product = product_model.browse(line.get("product_id", False))
if (
line.get("account_id")
!= product.categ_id.property_stock_valuation_account_id.id
):
current_move = self.browse(line.get("move_id", False))
current_rma = current_move.invoice_line_ids.filtered(
lambda x, product=product: x.rma_line_id
and x.product_id.id == product.id
).mapped("rma_line_id")
if len(current_rma) == 1:
line.update({"rma_line_id": current_rma.id})
elif len(current_rma) > 1:
find_with_label_rma = current_rma.filtered(
lambda x, line=line: x.name == line.get("name")
)
if len(find_with_label_rma) == 1:
line.update({"rma_line_id": find_with_label_rma.id})
return res

def _stock_account_get_last_step_stock_moves(self):
rslt = super()._stock_account_get_last_step_stock_moves()
for invoice in self.filtered(lambda x: x.move_type == "out_invoice"):
rslt += invoice.mapped("line_ids.rma_line_id.move_ids").filtered(
lambda x: x.state == "done" and x.location_dest_id.usage == "customer"
)
for invoice in self.filtered(lambda x: x.move_type == "out_refund"):
# Add refunds generated from the RMA
rslt += invoice.mapped("line_ids.rma_line_id.move_ids").filtered(
lambda x: x.state == "done" and x.location_id.usage == "customer"
)
return rslt
Loading

0 comments on commit 62a51f1

Please sign in to comment.