Merge PR #447 into 17.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2025-02-12 16:38:55 +00:00
11 changed files with 287 additions and 126 deletions

View File

@@ -87,16 +87,16 @@ To use this module, you need to:
quantity to another RMA, preview the RMA in the website. All of these
operations can be done by clicking on the buttons in the status bar.
- If you click on 'To Refund' button, a refund will be created, and
it will be accessible via the smart button labeled Refund. The RMA
will be set automatically to 'Refunded' state when the refund is
validated.
- If you click on 'Replace' or 'Return to customer' button instead, a
popup wizard will guide you to create a Delivery order to the
client and this order will be accessible via the smart button
labeled Delivery. The RMA will be set automatically to 'Replaced'
or 'Returned' state when the RMA quantity is equal or lower than
the quantity in done delivery orders linked to it.
- If you click on 'To Refund' button, a refund will be created, and
it will be accessible via the smart button labeled Refund. The RMA
will be set automatically to 'Refunded' state when the refund is
validated.
- If you click on 'Replace' or 'Return to customer' button instead,
a popup wizard will guide you to create a Delivery order to the
client and this order will be accessible via the smart button
labeled Delivery. The RMA will be set automatically to 'Replaced'
or 'Returned' state when the RMA quantity is equal or lower than
the quantity in done delivery orders linked to it.
6. You can also finish the RMA without further ado. To do so click on
the *Finish* button. A wizard will ask you for the reason from a
@@ -117,10 +117,10 @@ An RMA can also be created from a return of a delivery order:
There are Optional RMA Teams that can be used for:
- Organize RMAs in sections.
- Subscribe users to notifications.
- Create RMAs from incoming mail to special aliases (See
configuration section).
- Organize RMAs in sections.
- Subscribe users to notifications.
- Create RMAs from incoming mail to special aliases (See
configuration section).
To create an RMA Team (RMA Responsible user level required):
@@ -128,8 +128,8 @@ To create an RMA Team (RMA Responsible user level required):
2. Create a new team and assign a name, a responsible and members.
3. Subscribe users to notifications, that can be of these subtypes:
- RMA draft. When a new RMA is created.
- Notes, Debates, Activities. As in standard Odoo.
- RMA draft. When a new RMA is created.
- Notes, Debates, Activities. As in standard Odoo.
4. In the list view, use the cross handle to sort RMA Teams. The top
team will be the default one if no team is set.
@@ -137,12 +137,12 @@ To create an RMA Team (RMA Responsible user level required):
Known issues / Roadmap
======================
- As soon as the picking is selected, the user should select the move,
but perhaps stock.move \_rec_name could be improved to better show
what the product of that move is.
- Add RMA reception and/or RMA delivery on several steps - 2 or 3 - like
normal receptions/deliveries. It should be a separate option inside
the warehouse definition.
- As soon as the picking is selected, the user should select the move,
but perhaps stock.move \_rec_name could be improved to better show
what the product of that move is.
- Add RMA reception and/or RMA delivery on several steps - 2 or 3 -
like normal receptions/deliveries. It should be a separate option
inside the warehouse definition.
Bug Tracker
===========
@@ -165,20 +165,22 @@ Authors
Contributors
------------
- `Tecnativa <https://www.tecnativa.com>`__:
- `Tecnativa <https://www.tecnativa.com>`__:
- Ernesto Tejeda
- Pedro M. Baeza
- David Vidal
- Víctor Martínez
- Ernesto Tejeda
- Pedro M. Baeza
- David Vidal
- Víctor Martínez
- Chafique Delli <chafique.delli@akretion.com>
- Giovanni Serra - Ooops <giovanni@ooops404.com>
- `APSL-Nagarro <https://www.apsl.tech>`__:
- Chafique Delli <chafique.delli@akretion.com>
- Giovanni Serra - Ooops <giovanni@ooops404.com>
- `APSL-Nagarro <https://www.apsl.tech>`__:
- Antoni Marroig <amarroig@apsl.net>
- Antoni Marroig <amarroig@apsl.net>
- Michael Tietz (MT Software) mtietz@mt-software.de
- Michael Tietz (MT Software) mtietz@mt-software.de
- Jacques-Etienne Baudoux - BCIM je@bcim.be
- Souheil Bejaoui - ACSONE SA/NV souheil.bejaoui@acsone.eu
Maintainers
-----------

View File

@@ -856,6 +856,7 @@ class Rma(models.Model):
"partner_invoice_id",
"product_id",
"location_id",
"operation_id",
]
for record in self:
desc = ""

View File

@@ -26,9 +26,7 @@ class StockMove(models.Model):
)
# RMA that creates the out move
rma_id = fields.Many2one(
comodel_name="rma",
string="RMA return",
copy=False,
comodel_name="rma", string="RMA return", copy=False, index=True
)
def unlink(self):

View File

@@ -8,3 +8,5 @@
- [APSL-Nagarro](https://www.apsl.tech):
- Antoni Marroig \<<amarroig@apsl.net>\>
- Michael Tietz (MT Software) <mtietz@mt-software.de>
- Jacques-Etienne Baudoux - BCIM <je@bcim.be>
- Souheil Bejaoui - ACSONE SA/NV <souheil.bejaoui@acsone.eu>

View File

@@ -439,8 +439,8 @@ operations can be done by clicking on the buttons in the status bar.<ul>
it will be accessible via the smart button labeled Refund. The RMA
will be set automatically to Refunded state when the refund is
validated.</li>
<li>If you click on Replace or Return to customer button instead, a
popup wizard will guide you to create a Delivery order to the
<li>If you click on Replace or Return to customer button instead,
a popup wizard will guide you to create a Delivery order to the
client and this order will be accessible via the smart button
labeled Delivery. The RMA will be set automatically to Replaced
or Returned state when the RMA quantity is equal or lower than
@@ -494,9 +494,9 @@ team will be the default one if no team is set.</li>
<li>As soon as the picking is selected, the user should select the move,
but perhaps stock.move _rec_name could be improved to better show
what the product of that move is.</li>
<li>Add RMA reception and/or RMA delivery on several steps - 2 or 3 - like
normal receptions/deliveries. It should be a separate option inside
the warehouse definition.</li>
<li>Add RMA reception and/or RMA delivery on several steps - 2 or 3 -
like normal receptions/deliveries. It should be a separate option
inside the warehouse definition.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
@@ -532,6 +532,8 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
</ul>
</li>
<li>Michael Tietz (MT Software) <a class="reference external" href="mailto:mtietz&#64;mt-software.de">mtietz&#64;mt-software.de</a></li>
<li>Jacques-Etienne Baudoux - BCIM <a class="reference external" href="mailto:je&#64;bcim.be">je&#64;bcim.be</a></li>
<li>Souheil Bejaoui - ACSONE SA/NV <a class="reference external" href="mailto:souheil.bejaoui&#64;acsone.eu">souheil.bejaoui&#64;acsone.eu</a></li>
</ul>
</div>
<div class="section" id="maintainers">

View File

@@ -71,8 +71,11 @@ class TestRma(BaseCommon):
cls.warehouse = cls.env.ref("stock.warehouse0")
# Ensure grouping
cls.env.company.rma_return_grouping = True
cls.operation = cls.env.ref("rma.rma_operation_replace")
def _create_rma(self, partner=None, product=None, qty=None, location=None):
def _create_rma(
self, partner=None, product=None, qty=None, location=None, operation=None
):
vals = {}
if partner:
vals["partner_id"] = partner.id
@@ -82,14 +85,17 @@ class TestRma(BaseCommon):
vals["product_uom_qty"] = qty
if location:
vals["location_id"] = location.id
if operation:
vals["operation_id"] = operation.id
elif operation is None:
vals["operation_id"] = self.operation.id
vals["user_id"] = self.env.user.id
return self.env["rma"].create(vals)
def _create_confirm_receive(
self, partner=None, product=None, qty=None, location=None
self, partner=None, product=None, qty=None, location=None, operation=None
):
rma = self._create_rma(partner, product, qty, location)
rma = self._create_rma(partner, product, qty, location, operation)
rma.action_confirm()
rma.reception_move_id.quantity = rma.product_uom_qty
rma.reception_move_id.picking_id.button_validate()
@@ -221,19 +227,26 @@ class TestRmaCase(TestRma):
self.assertEqual(rma.product_uom, self.product.uom_id)
def test_ensure_required_fields_on_confirm(self):
rma = self._create_rma()
rma = self._create_rma(operation=False)
with self.assertRaises(ValidationError) as e:
rma.action_confirm()
self.assertEqual(
e.exception.args[0],
"Required field(s):\nCustomer\nShipping Address\nInvoice Address\nProduct",
"Required field(s):\nCustomer\nShipping Address\nInvoice Address\nProduct"
"\nRequested operation",
)
rma.partner_id = self.partner.id
with self.assertRaises(ValidationError) as e:
rma.action_confirm()
self.assertEqual(e.exception.args[0], "Required field(s):\nProduct")
self.assertEqual(
e.exception.args[0], "Required field(s):\nProduct\nRequested operation"
)
rma.product_id = self.product.id
rma.location_id = self.rma_loc.id
with self.assertRaises(ValidationError) as e:
rma.action_confirm()
self.assertEqual(e.exception.args[0], "Required field(s):\nRequested operation")
rma.operation_id = self.operation
rma.action_confirm()
self.assertEqual(rma.state, "confirmed")
@@ -670,6 +683,7 @@ class TestRmaCase(TestRma):
)
)
stock_return_picking_form.create_rma = True
stock_return_picking_form.rma_operation_id = self.operation
return_wizard = stock_return_picking_form.save()
picking_action = return_wizard.create_returns()
# Each origin move is linked to a different RMA
@@ -698,6 +712,7 @@ class TestRmaCase(TestRma):
rma_form.move_id = origin_delivery.move_ids.filtered(
lambda r: r.product_id == self.product
)
rma_form.operation_id = self.operation
rma = rma_form.save()
rma.action_confirm()
rma.reception_move_id.quantity = 10

View File

@@ -2,89 +2,184 @@
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="stock.res_config_settings_view_form" />
<field name="inherit_id" ref="base.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath
expr="//block[@name='operations_setting_container']"
position="inside"
>
<setting
title="Finish RMAs manually"
help=" When the RMA is receive, allow to finsish it manually choosing a finalization reason."
>
<field name="group_rma_manual_finalization" />
</setting>
<setting
title="Values set here are company-specific."
help="Group RMA returns by customer and warehouse."
>
<field name="rma_return_grouping" />
</setting>
<setting
title="Send automatic RMA info to customer"
help="When the RMA is confirmed, send an automatic information email."
>
<field name="send_rma_confirmation" />
<div class="row mt16" invisible="not send_rma_confirmation">
<label
for="rma_mail_confirmation_template_id"
string="Email Template"
class="col-lg-4 o_light_label"
/>
<field
name="rma_mail_confirmation_template_id"
class="oe_inline"
required="send_rma_confirmation"
context="{'default_model': 'rma'}"
/>
</div>
</setting>
<setting
title="Send automatic RMA products reception notification to customer"
help="When the RMA products are received, send an automatic information email."
>
<field name="send_rma_receipt_confirmation" />
<div
class="row mt16"
invisible="not send_rma_receipt_confirmation"
<xpath expr="//form" position="inside">
<app string="RMA" groups="rma.rma_group_manager" name="rma">
<h2>Return Merchandise Authorization Management</h2>
<div
class="row mt16 o_settings_container"
name="operations_setting_container"
>
<label
for="rma_mail_receipt_confirmation_template_id"
string="Email Template"
class="col-lg-4 o_light_label"
/>
<field
name="rma_mail_receipt_confirmation_template_id"
class="oe_inline"
required="send_rma_receipt_confirmation"
context="{'default_model': 'rma'}"
/>
</div>
</setting>
<setting
title="Send automatic notification when the customer places an RMA"
help="When customers themselves place an RMA from the portal, send an automatic notification acknowleging it."
>
<field name="send_rma_draft_confirmation" />
<div
class="row mt16"
invisible="not send_rma_draft_confirmation"
>
<label
for="rma_mail_draft_confirmation_template_id"
string="Email Template"
class="col-lg-4 o_light_label"
/>
<field
name="rma_mail_draft_confirmation_template_id"
class="oe_inline"
required="send_rma_draft_confirmation"
context="{'default_model': 'rma'}"
/>
class="col-12 col-lg-6 o_setting_box"
title="Finish RMAs manually"
>
<div class="o_setting_left_pane">
<field name="group_rma_manual_finalization" />
</div>
<div class="o_setting_right_pane">
<label
for="group_rma_manual_finalization"
string="RMA Manual Finalization"
/>
<div class="text-muted">
When the RMA is receive, allow to finsish it manually choosing
a finalization reason.
</div>
</div>
</div>
</setting>
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="rma_return_grouping" />
</div>
<div class="o_setting_right_pane">
<label for="rma_return_grouping" />
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div
class="text-muted"
>Group RMA returns by customer and warehouse.</div>
</div>
</div>
<div
class="col-12 col-lg-6 o_setting_box"
title="Send automatic RMA info to customer"
>
<div class="o_setting_left_pane">
<field name="send_rma_confirmation" />
</div>
<div class="o_setting_right_pane">
<label
for="send_rma_confirmation"
string="RMA Confirmation Email"
/>
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div
class="text-muted"
>When the RMA is confirmed, send an automatic information email.</div>
<div
class="row mt16"
invisible="not send_rma_confirmation"
>
<label
for="rma_mail_confirmation_template_id"
string="Email Template"
class="col-lg-4 o_light_label"
/>
<field
name="rma_mail_confirmation_template_id"
class="oe_inline"
required="send_rma_confirmation"
context="{'default_model': 'rma'}"
/>
</div>
</div>
</div>
<div
class="col-12 col-lg-6 o_setting_box"
title="Send automatic RMA products reception notification to customer"
>
<div class="o_setting_left_pane">
<field name="send_rma_receipt_confirmation" />
</div>
<div class="o_setting_right_pane">
<label
for="send_rma_receipt_confirmation"
string="RMA Receipt Confirmation Email"
/>
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div
class="text-muted"
>When the RMA products are received, send an automatic information email.</div>
<div
class="row mt16"
invisible="not send_rma_receipt_confirmation"
>
<label
for="rma_mail_receipt_confirmation_template_id"
string="Email Template"
class="col-lg-4 o_light_label"
/>
<field
name="rma_mail_receipt_confirmation_template_id"
class="oe_inline"
required="send_rma_receipt_confirmation"
context="{'default_model': 'rma'}"
/>
</div>
</div>
</div>
<div
class="col-12 col-lg-6 o_setting_box"
title="Send automatic notification when the customer places an RMA"
>
<div class="o_setting_left_pane">
<field name="send_rma_draft_confirmation" />
</div>
<div class="o_setting_right_pane">
<label
for="send_rma_draft_confirmation"
string="RMA draft notification Email"
/>
<span
class="fa fa-lg fa-building-o"
title="Values set here are company-specific."
groups="base.group_multi_company"
/>
<div
class="text-muted"
>When customers themselves place an RMA from the portal, send an automatic notification acknowleging it.</div>
<div
class="row mt16"
invisible="not send_rma_draft_confirmation"
>
<label
for="rma_mail_draft_confirmation_template_id"
string="Email Template"
class="col-lg-4 o_light_label"
/>
<field
name="rma_mail_draft_confirmation_template_id"
class="oe_inline"
required="send_rma_draft_confirmation"
context="{'default_model': 'rma'}"
/>
</div>
</div>
</div>
</div>
</app>
</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', 'bin_size': False}</field>
</record>
<menuitem
id="menu_rma_general_settings"
name="Settings"
parent="rma_configuration_menu"
sequence="0"
action="action_rma_config_settings"
groups="base.group_system"
/>
</odoo>

View File

@@ -9,8 +9,26 @@
<field name="arch" type="xml">
<search>
<field name="name" />
<field name="origin" />
<field name="user_id" />
<field name="product_id" />
<field name="tag_ids" />
<filter
string="Awaiting Action"
name="waiting_action"
domain="[('state', 'in', ['waiting_return', 'waiting_replacement', 'confirmed'])]"
/>
<filter
string="Processed"
name="processed"
domain="[('state', 'in', ['received', 'refunded', 'replaced', 'finished'])]"
/>
<filter
string="Closed"
name="closed"
domain="[('state', 'in', ['locked', 'cancelled'])]"
/>
<separator />
<filter
name="draft_filter"
string="Draft"

View File

@@ -11,6 +11,20 @@ from odoo.tools import float_compare
class ReturnPickingLine(models.TransientModel):
_inherit = "stock.return.picking.line"
rma_operation_id = fields.Many2one(
comodel_name="rma.operation",
string="Operation",
compute="_compute_rma_operation_id",
store=True,
readonly=False,
)
@api.depends("wizard_id.rma_operation_id")
def _compute_rma_operation_id(self):
for rec in self:
if rec.wizard_id.rma_operation_id:
rec.rma_operation_id = rec.wizard_id.rma_operation_id
def _prepare_rma_vals(self):
self.ensure_one()
return {
@@ -19,6 +33,7 @@ class ReturnPickingLine(models.TransientModel):
"product_uom_qty": self.quantity,
"product_uom": self.product_id.uom_id.id,
"location_id": self.wizard_id.location_id.id or self.move_id.location_id.id,
"operation_id": self.rma_operation_id.id,
}
@@ -30,6 +45,10 @@ class ReturnPicking(models.TransientModel):
rma_location_ids = fields.Many2many(
comodel_name="stock.location", compute="_compute_rma_location_id"
)
rma_operation_id = fields.Many2one(
comodel_name="rma.operation",
string="Requested operation",
)
# Expand domain for RMAs
location_id = fields.Many2one(
domain="create_rma and [('id', 'child_of', rma_location_ids)]"

View File

@@ -8,12 +8,20 @@
<field name="model">stock.return.picking</field>
<field name="inherit_id" ref="stock.view_stock_return_picking_form" />
<field name="arch" type="xml">
<field name="product_return_moves" position="after">
<xpath expr="//field[@name='product_return_moves']//tree" position="inside">
<field
name="rma_operation_id"
required="parent.create_rma and quantity>0"
column_invisible="not parent.create_rma"
/>
</xpath>
<field name="product_return_moves" position="before">
<group name="group_rma">
<field
name="create_rma"
invisible="picking_type_code != 'outgoing'"
/>
<field name="rma_operation_id" invisible="not create_rma" />
<field name="rma_location_ids" invisible="1" />
<field name="picking_id" invisible="1" />
<field name="picking_type_code" invisible="1" />

View File

@@ -77,6 +77,7 @@ class TestRMALot(TransactionCase):
)
)
stock_return_picking_form.create_rma = True
stock_return_picking_form.rma_operation_id = self.operation
return_wizard = stock_return_picking_form.save()
self.assertEqual(len(return_wizard.product_return_moves), 2)
return_wizard.create_returns()