mirror of
https://github.com/OCA/rma.git
synced 2025-02-16 17:11:47 +02:00
@@ -29,6 +29,7 @@
|
||||
"views/rma_views.xml",
|
||||
"views/stock_picking_views.xml",
|
||||
"views/stock_warehouse_views.xml",
|
||||
"views/res_config_settings_views.xml",
|
||||
],
|
||||
"post_init_hook": "post_init_hook",
|
||||
"application": True,
|
||||
|
||||
@@ -7,6 +7,12 @@
|
||||
<field name="default" eval="False" />
|
||||
<field name="description">RMA in draft state</field>
|
||||
</record>
|
||||
<record id="mt_rma_notification" model="mail.message.subtype">
|
||||
<field name="name">RMA Notificatoin</field>
|
||||
<field name="res_model">rma</field>
|
||||
<field name="default" eval="False" />
|
||||
<field name="description">RMA automatic customer notifications</field>
|
||||
</record>
|
||||
<!-- rma_team-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_rma_team_rma_draft" model="mail.message.subtype">
|
||||
<field name="name">Draft RMA</field>
|
||||
@@ -16,6 +22,14 @@
|
||||
<field name="parent_id" eval="ref('rma.mt_rma_draft')" />
|
||||
<field name="relation_field">team_id</field>
|
||||
</record>
|
||||
<record id="mt_rma_team_rma_notification" model="mail.message.subtype">
|
||||
<field name="name">RMA Notification</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="res_model">rma.team</field>
|
||||
<field name="default" eval="True" />
|
||||
<field name="parent_id" eval="ref('rma.mt_rma_notification')" />
|
||||
<field name="relation_field">team_id</field>
|
||||
</record>
|
||||
<!--RMA email template -->
|
||||
<record id="mail_template_rma_notification" model="mail.template">
|
||||
<field name="name">RMA Notification</field>
|
||||
|
||||
@@ -5,6 +5,7 @@ from . import rma
|
||||
from . import rma_operation
|
||||
from . import rma_team
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
from . import res_users
|
||||
from . import stock_move
|
||||
|
||||
@@ -9,14 +9,13 @@ from odoo.tools import float_compare
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
def post(self):
|
||||
""" Avoids to validate a refund with less quantity of product than
|
||||
quantity in the linked RMA.
|
||||
"""
|
||||
def _check_rma_invoice_lines_qty(self):
|
||||
"""We can't refund a different qty than the stated in the RMA.
|
||||
Extend to change criteria """
|
||||
precision = self.env["decimal.precision"].precision_get(
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
if (
|
||||
return (
|
||||
self.sudo()
|
||||
.mapped("invoice_line_ids")
|
||||
.filtered(
|
||||
@@ -26,7 +25,13 @@ class AccountMove(models.Model):
|
||||
< 0
|
||||
)
|
||||
)
|
||||
):
|
||||
)
|
||||
|
||||
def post(self):
|
||||
""" Avoids to validate a refund with less quantity of product than
|
||||
quantity in the linked RMA.
|
||||
"""
|
||||
if self._check_rma_invoice_lines_qty():
|
||||
raise ValidationError(
|
||||
_(
|
||||
"There is at least one invoice lines whose quantity is "
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, models
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
def _default_rma_mail_confirmation_template(self):
|
||||
try:
|
||||
return self.env.ref("rma.mail_template_rma_notification").id
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
send_rma_confirmation = fields.Boolean(
|
||||
string="Send RMA Confirmation",
|
||||
help="When the delivery is confirmed, send a confirmation email "
|
||||
"to the customer.",
|
||||
)
|
||||
rma_mail_confirmation_template_id = fields.Many2one(
|
||||
comodel_name="mail.template",
|
||||
string="Email Template confirmation for RMA",
|
||||
domain="[('model', '=', 'rma')]",
|
||||
default=_default_rma_mail_confirmation_template,
|
||||
help="Email sent to the customer once the RMA is confirmed.",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
company = super(Company, self).create(vals)
|
||||
|
||||
14
rma/models/res_config_settings.py
Normal file
14
rma/models/res_config_settings.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright 2021 Tecnativa - David Vidal
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = "res.config.settings"
|
||||
|
||||
send_rma_confirmation = fields.Boolean(
|
||||
related="company_id.send_rma_confirmation", readonly=False,
|
||||
)
|
||||
rma_mail_confirmation_template_id = fields.Many2one(
|
||||
related="company_id.rma_mail_confirmation_template_id", readonly=False,
|
||||
)
|
||||
@@ -73,6 +73,13 @@ class Rma(models.Model):
|
||||
index=True,
|
||||
tracking=True,
|
||||
)
|
||||
partner_shipping_id = fields.Many2one(
|
||||
string="Shipping Address",
|
||||
comodel_name="res.partner",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
help="Shipping address for current RMA.",
|
||||
)
|
||||
partner_invoice_id = fields.Many2one(
|
||||
string="Invoice Address",
|
||||
comodel_name="res.partner",
|
||||
@@ -385,7 +392,9 @@ class Rma(models.Model):
|
||||
record.access_url = "/my/rmas/{}".format(record.id)
|
||||
|
||||
# Constrains methods (@api.constrains)
|
||||
@api.constrains("state", "partner_id", "partner_invoice_id", "product_id")
|
||||
@api.constrains(
|
||||
"state", "partner_id", "partner_shipping_id", "partner_invoice_id", "product_id"
|
||||
)
|
||||
def _check_required_after_draft(self):
|
||||
""" Check that RMAs are being created or edited with the
|
||||
necessary fields filled out. Only applies to 'Draft' and
|
||||
@@ -420,10 +429,13 @@ class Rma(models.Model):
|
||||
def _onchange_partner_id(self):
|
||||
self.picking_id = False
|
||||
partner_invoice_id = False
|
||||
partner_shipping_id = False
|
||||
if self.partner_id:
|
||||
address = self.partner_id.address_get(["invoice"])
|
||||
address = self.partner_id.address_get(["invoice", "delivery"])
|
||||
partner_invoice_id = address.get("invoice", False)
|
||||
partner_shipping_id = address.get("delivery", False)
|
||||
self.partner_invoice_id = partner_invoice_id
|
||||
self.partner_shipping_id = partner_shipping_id
|
||||
|
||||
@api.onchange("picking_id")
|
||||
def _onchange_picking_id(self):
|
||||
@@ -498,13 +510,25 @@ class Rma(models.Model):
|
||||
)
|
||||
return super().unlink()
|
||||
|
||||
def _send_confirmation_email(self):
|
||||
"""Auto send notifications"""
|
||||
for rma in self.filtered(lambda p: p.company_id.send_rma_confirmation):
|
||||
rma_template_id = rma.company_id.rma_mail_confirmation_template_id.id
|
||||
rma.with_context(
|
||||
force_send=True,
|
||||
mark_rma_as_sent=True,
|
||||
default_subtype_id=self.env.ref("rma.mt_rma_notification").id,
|
||||
).message_post_with_template(rma_template_id)
|
||||
|
||||
# Action methods
|
||||
def action_rma_send(self):
|
||||
self.ensure_one()
|
||||
template = self.env.ref("rma.mail_template_rma_notification", False)
|
||||
template = self.company_id.rma_mail_confirmation_template_id or template
|
||||
form = self.env.ref("mail.email_compose_message_wizard_form", False)
|
||||
ctx = {
|
||||
"default_model": "rma",
|
||||
"default_subtype_id": self.env.ref("rma.mt_rma_notification").id,
|
||||
"default_res_id": self.ids[0],
|
||||
"default_use_template": bool(template),
|
||||
"default_template_id": template and template.id or False,
|
||||
@@ -536,6 +560,7 @@ class Rma(models.Model):
|
||||
self.write({"reception_move_id": reception_move.id, "state": "confirmed"})
|
||||
if self.partner_id not in self.message_partner_ids:
|
||||
self.message_subscribe([self.partner_id.id])
|
||||
self._send_confirmation_email()
|
||||
|
||||
def action_refund(self):
|
||||
"""Invoked when 'Refund' button in rma form view is clicked
|
||||
@@ -710,7 +735,10 @@ class Rma(models.Model):
|
||||
# Validation business methods
|
||||
def _ensure_required_fields(self):
|
||||
""" This method is used to ensure the following fields are not empty:
|
||||
['partner_id', 'partner_invoice_id', 'product_id', 'location_id']
|
||||
[
|
||||
'partner_id', 'partner_invoice_id', 'partner_shipping_id',
|
||||
'product_id', 'location_id'
|
||||
]
|
||||
|
||||
This method is intended to be called on confirm RMA action and is
|
||||
invoked by:
|
||||
@@ -718,7 +746,13 @@ class Rma(models.Model):
|
||||
rma.action_confirm
|
||||
"""
|
||||
ir_translation = self.env["ir.translation"]
|
||||
required = ["partner_id", "partner_invoice_id", "product_id", "location_id"]
|
||||
required = [
|
||||
"partner_id",
|
||||
"partner_shipping_id",
|
||||
"partner_invoice_id",
|
||||
"product_id",
|
||||
"location_id",
|
||||
]
|
||||
for record in self:
|
||||
desc = ""
|
||||
for field in filter(lambda item: not record[item], required):
|
||||
@@ -850,7 +884,8 @@ class Rma(models.Model):
|
||||
|
||||
def _prepare_picking(self, picking_form):
|
||||
picking_form.origin = self.name
|
||||
picking_form.partner_id = self.partner_id
|
||||
picking_form.partner_id = self.partner_shipping_id
|
||||
picking_form.location_id = self.partner_shipping_id.property_stock_customer
|
||||
picking_form.location_dest_id = self.location_id
|
||||
with picking_form.move_ids_without_package.new() as move_form:
|
||||
move_form.product_id = self.product_id
|
||||
@@ -915,16 +950,32 @@ class Rma(models.Model):
|
||||
rma.action_refund
|
||||
"""
|
||||
self.ensure_one()
|
||||
line_form.product_id = self.product_id
|
||||
line_form.quantity = self.product_uom_qty
|
||||
line_form.product_uom_id = self.product_uom
|
||||
product = self._get_refund_line_product()
|
||||
qty, uom = self._get_refund_line_quantity()
|
||||
line_form.product_id = product
|
||||
line_form.quantity = qty
|
||||
line_form.product_uom_id = uom
|
||||
line_form.price_unit = self._get_refund_line_price_unit()
|
||||
|
||||
def _get_refund_line_product(self):
|
||||
"""To be overriden in a third module with the proper origin values
|
||||
in case a kit is linked with the rma"""
|
||||
return self.product_id
|
||||
|
||||
def _get_refund_line_quantity(self):
|
||||
"""To be overriden in a third module with the proper origin values
|
||||
in case a kit is linked with the rma """
|
||||
return (self.product_uom_qty, self.product_uom)
|
||||
|
||||
def _get_refund_line_price_unit(self):
|
||||
"""To be overriden in a third module with the proper origin values
|
||||
in case a sale order is linked to the original move"""
|
||||
return self.product_id.lst_price
|
||||
|
||||
def _get_extra_refund_line_vals(self):
|
||||
"""Override to write aditional stuff into the refund line"""
|
||||
return {}
|
||||
|
||||
# Returning business methods
|
||||
def create_return(self, scheduled_date, qty=None, uom=None):
|
||||
"""Intended to be invoked by the delivery wizard"""
|
||||
@@ -933,7 +984,11 @@ class Rma(models.Model):
|
||||
group_dict = {}
|
||||
rmas_to_return = self.filtered("can_be_returned")
|
||||
for record in rmas_to_return:
|
||||
key = (record.partner_id.id, record.company_id.id, record.warehouse_id)
|
||||
key = (
|
||||
record.partner_shipping_id.id,
|
||||
record.company_id.id,
|
||||
record.warehouse_id,
|
||||
)
|
||||
group_dict.setdefault(key, self.env["rma"])
|
||||
group_dict[key] |= record
|
||||
for rmas in group_dict.values():
|
||||
@@ -981,7 +1036,7 @@ class Rma(models.Model):
|
||||
def _prepare_returning_picking(self, picking_form, origin=None):
|
||||
picking_form.picking_type_id = self.warehouse_id.rma_out_type_id
|
||||
picking_form.origin = origin or self.name
|
||||
picking_form.partner_id = self.partner_id
|
||||
picking_form.partner_id = self.partner_shipping_id
|
||||
|
||||
def _prepare_returning_move(
|
||||
self, move_form, scheduled_date, quantity=None, uom=None
|
||||
@@ -1047,7 +1102,7 @@ class Rma(models.Model):
|
||||
{
|
||||
"name": self.name,
|
||||
"move_type": "direct",
|
||||
"partner_id": self.partner_id.id,
|
||||
"partner_id": self.partner_shipping_id.id,
|
||||
}
|
||||
)
|
||||
.id
|
||||
@@ -1059,7 +1114,7 @@ class Rma(models.Model):
|
||||
product,
|
||||
qty,
|
||||
uom,
|
||||
self.partner_id.property_stock_customer,
|
||||
self.partner_shipping_id.property_stock_customer,
|
||||
self.product_id.display_name,
|
||||
self.procurement_group_id.name,
|
||||
self.company_id,
|
||||
@@ -1077,18 +1132,27 @@ class Rma(models.Model):
|
||||
"group_id": group_id,
|
||||
"date_planned": scheduled_date,
|
||||
"warehouse_id": warehouse,
|
||||
"partner_id": self.partner_id.id,
|
||||
"partner_id": self.partner_shipping_id.id,
|
||||
"rma_id": self.id,
|
||||
"priority": self.priority,
|
||||
}
|
||||
|
||||
# Mail business methods
|
||||
def _creation_subtype(self):
|
||||
if self.state in ("draft", "confirmed"):
|
||||
if self.state in ("draft"):
|
||||
return self.env.ref("rma.mt_rma_draft")
|
||||
else:
|
||||
return super()._creation_subtype()
|
||||
|
||||
def _track_subtype(self, init_values):
|
||||
self.ensure_one()
|
||||
if "state" in init_values:
|
||||
if self.state == "draft":
|
||||
return self.env.ref("rma.mt_rma_draft")
|
||||
elif self.state == "confirmed":
|
||||
return self.env.ref("rma.mt_rma_notification")
|
||||
return super()._track_subtype(init_values)
|
||||
|
||||
def message_new(self, msg_dict, custom_values=None):
|
||||
"""Extract the needed values from an incoming rma emails data-set
|
||||
to be used to create an RMA.
|
||||
|
||||
@@ -96,13 +96,16 @@ class StockMove(models.Model):
|
||||
partner = original_picking.partner_id
|
||||
if hasattr(original_picking, "sale_id") and original_picking.sale_id:
|
||||
partner_invoice_id = original_picking.sale_id.partner_invoice_id.id
|
||||
partner_shipping_id = original_picking.sale_id.partner_shipping_id.id
|
||||
else:
|
||||
partner_invoice_id = (
|
||||
partner.address_get(["invoice"]).get("invoice", False),
|
||||
partner_invoice_id = partner.address_get(["invoice"]).get("invoice", False)
|
||||
partner_shipping_id = partner.address_get(["delivery"]).get(
|
||||
"delivery", False
|
||||
)
|
||||
return {
|
||||
"user_id": self.env.user.id,
|
||||
"partner_id": partner.id,
|
||||
"partner_shipping_id": partner_shipping_id,
|
||||
"partner_invoice_id": partner_invoice_id,
|
||||
"origin": original_picking.name,
|
||||
"picking_id": original_picking.id,
|
||||
|
||||
@@ -44,6 +44,13 @@ class TestRma(SavepointCase):
|
||||
"type": "invoice",
|
||||
}
|
||||
)
|
||||
cls.partner_shipping = cls.res_partner.create(
|
||||
{
|
||||
"name": "Partner shipping test",
|
||||
"parent_id": cls.partner.id,
|
||||
"type": "delivery",
|
||||
}
|
||||
)
|
||||
|
||||
def _create_rma(self, partner=None, product=None, qty=None, location=None):
|
||||
rma_form = Form(self.env["rma"])
|
||||
@@ -181,7 +188,8 @@ class TestRma(SavepointCase):
|
||||
rma.action_confirm()
|
||||
self.assertEqual(
|
||||
e.exception.name,
|
||||
"Required field(s):\nCustomer\nInvoice Address\nProduct\nLocation",
|
||||
"Required field(s):\nCustomer\nShipping Address\nInvoice Address\n"
|
||||
"Product\nLocation",
|
||||
)
|
||||
with Form(rma) as rma_form:
|
||||
rma_form.partner_id = self.partner
|
||||
@@ -531,7 +539,7 @@ class TestRma(SavepointCase):
|
||||
# One picking per partner
|
||||
self.assertNotEqual(pick_1.partner_id, pick_2.partner_id)
|
||||
self.assertEqual(
|
||||
pick_1.partner_id, (rma_1 | rma_2 | rma_3).mapped("partner_id"),
|
||||
pick_1.partner_id, (rma_1 | rma_2 | rma_3).mapped("partner_shipping_id"),
|
||||
)
|
||||
self.assertEqual(pick_2.partner_id, rma_4.partner_id)
|
||||
# Each RMA of (rma_1, rma_2 and rma_3) is linked to a different
|
||||
@@ -656,3 +664,21 @@ class TestRma(SavepointCase):
|
||||
def test_quantities_on_hand(self):
|
||||
rma = self._create_confirm_receive(self.partner, self.product, 10, self.rma_loc)
|
||||
self.assertEqual(rma.product_id.qty_available, 0)
|
||||
|
||||
def test_autoconfirm_email(self):
|
||||
rma = self._create_rma(self.partner, self.product, 10, self.rma_loc)
|
||||
rma.company_id.send_rma_confirmation = True
|
||||
rma.company_id.rma_mail_confirmation_template_id = self.env.ref(
|
||||
"rma.mail_template_rma_notification"
|
||||
)
|
||||
previous_mails = self.env["mail.mail"].search(
|
||||
[("partner_ids", "in", self.partner.ids)]
|
||||
)
|
||||
self.assertFalse(previous_mails)
|
||||
rma.action_confirm()
|
||||
mail = self.env["mail.message"].search(
|
||||
[("partner_ids", "in", self.partner.ids)]
|
||||
)
|
||||
self.assertTrue(rma.name in mail.subject)
|
||||
self.assertTrue(rma.name in mail.body)
|
||||
self.assertEqual(self.env.ref("rma.mt_rma_notification"), mail.subtype_id)
|
||||
|
||||
52
rma/views/res_config_settings_views.xml
Normal file
52
rma/views/res_config_settings_views.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<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="arch" type="xml">
|
||||
<xpath
|
||||
expr="//div[@data-key='stock']/div[hasclass('o_settings_container')]"
|
||||
position="inside"
|
||||
>
|
||||
<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"
|
||||
attrs="{'invisible': [('send_rma_confirmation', '=', False)]}"
|
||||
>
|
||||
<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"
|
||||
attrs="{'required': [('send_rma_confirmation', '=', True)]}"
|
||||
context="{'default_model': 'rma'}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -46,7 +46,7 @@
|
||||
<tr class="active">
|
||||
<th>RMA #</th>
|
||||
<th class='d-none d-md-table-cell'>Date</th>
|
||||
<th>Product</th>
|
||||
<th name="th_product">Product</th>
|
||||
<th class='text-right'>Quantity</th>
|
||||
<th class='d-none d-md-table-cell'>Status</th>
|
||||
</tr>
|
||||
@@ -66,7 +66,7 @@
|
||||
<span t-field="rma.date" />
|
||||
</td>
|
||||
<!-- Portal users don't have access to unpublished products -->
|
||||
<td>
|
||||
<td name="td_product">
|
||||
<span t-esc="rma.sudo().product_id.display_name" />
|
||||
</td>
|
||||
<td class='text-right'>
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
context="{'search_default_customer':1, 'show_address': 1, 'show_vat': True}"
|
||||
options="{'always_reload': True}"
|
||||
/>
|
||||
<field name="partner_shipping_id" />
|
||||
<field name="partner_invoice_id" />
|
||||
<field name="picking_id" options="{'no_create': True}" />
|
||||
<field
|
||||
@@ -353,7 +354,7 @@
|
||||
<field name="name">RMA</field>
|
||||
<field name="res_model">rma</field>
|
||||
<field name="view_mode">tree,form,pivot,calendar,activity</field>
|
||||
<field name="context">{"search_default_user_id": uid}</field>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Click to add a new RMA.
|
||||
|
||||
@@ -27,6 +27,7 @@ class CustomerPortal(CustomerPortal):
|
||||
wizard_obj = request.env["sale.order.rma.wizard"]
|
||||
# Set wizard line vals
|
||||
mapped_vals = {}
|
||||
partner_shipping_id = post.pop("partner_shipping_id", False)
|
||||
for name, value in post.items():
|
||||
row, field_name = name.split("-", 1)
|
||||
mapped_vals.setdefault(row, {}).update({field_name: value})
|
||||
@@ -38,9 +39,13 @@ class CustomerPortal(CustomerPortal):
|
||||
order = order_obj.browse(order_id).sudo()
|
||||
location_id = order.warehouse_id.rma_loc_id.id
|
||||
wizard = wizard_obj.with_context(active_id=order_id).create(
|
||||
{"line_ids": line_vals, "location_id": location_id}
|
||||
{
|
||||
"line_ids": line_vals,
|
||||
"location_id": location_id,
|
||||
"partner_shipping_id": partner_shipping_id,
|
||||
}
|
||||
)
|
||||
rma = wizard.sudo().create_rma()
|
||||
rma = wizard.sudo().create_rma(from_portal=True)
|
||||
for rec in rma:
|
||||
rec.origin += _(" (Portal)")
|
||||
# Add the user as follower of the created RMAs so they can
|
||||
|
||||
@@ -25,6 +25,7 @@ class Rma(models.Model):
|
||||
comodel_name="sale.order.line", compute="_compute_allowed_move_ids",
|
||||
)
|
||||
move_id = fields.Many2one(domain="[('id', 'in', allowed_move_ids)]")
|
||||
sale_line_id = fields.Many2one(related="move_id.sale_line_id",)
|
||||
allowed_product_ids = fields.Many2many(
|
||||
comodel_name="product.product", compute="_compute_allowed_product_ids",
|
||||
)
|
||||
@@ -83,3 +84,24 @@ class Rma(models.Model):
|
||||
if self.order_id:
|
||||
invoice_form.invoice_user_id = self.order_id.user_id
|
||||
return res
|
||||
|
||||
def _get_refund_line_price_unit(self):
|
||||
"""Get the sale order price unit"""
|
||||
if self.sale_line_id:
|
||||
return self.sale_line_id.price_unit
|
||||
return super()._get_refund_line_price_unit()
|
||||
|
||||
def _get_refund_line_product(self):
|
||||
"""To be overriden in a third module with the proper origin values
|
||||
in case a kit is linked with the rma """
|
||||
if not self.sale_line_id:
|
||||
return super()._get_refund_line_product()
|
||||
return self.sale_line_id.product_id
|
||||
|
||||
def _prepare_refund_line(self, line_form):
|
||||
"""Add line data"""
|
||||
super()._prepare_refund_line(line_form)
|
||||
line = self.sale_line_id
|
||||
if line:
|
||||
line_form.discount = line.discount
|
||||
line_form.sequence = line.sequence
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, fields, models
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
@@ -22,6 +22,16 @@ class SaleOrder(models.Model):
|
||||
for record in self:
|
||||
record.rma_count = mapped_data.get(record.id, 0)
|
||||
|
||||
def _prepare_rma_wizard_line_vals(self, data):
|
||||
"""So we can extend the wizard easily"""
|
||||
return {
|
||||
"product_id": data["product"].id,
|
||||
"quantity": data["quantity"],
|
||||
"sale_line_id": data["sale_line_id"].id,
|
||||
"uom_id": data["uom"].id,
|
||||
"picking_id": data["picking"] and data["picking"].id,
|
||||
}
|
||||
|
||||
def action_create_rma(self):
|
||||
self.ensure_one()
|
||||
if self.state not in ["sale", "done"]:
|
||||
@@ -30,17 +40,7 @@ class SaleOrder(models.Model):
|
||||
)
|
||||
wizard_obj = self.env["sale.order.rma.wizard"]
|
||||
line_vals = [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"product_id": data["product"].id,
|
||||
"quantity": data["quantity"],
|
||||
"sale_line_id": data["sale_line_id"].id,
|
||||
"uom_id": data["uom"].id,
|
||||
"picking_id": data["picking"] and data["picking"].id,
|
||||
},
|
||||
)
|
||||
(0, 0, self._prepare_rma_wizard_line_vals(data))
|
||||
for data in self.get_delivery_rma_data()
|
||||
]
|
||||
wizard = wizard_obj.with_context(active_id=self.id).create(
|
||||
@@ -76,6 +76,19 @@ class SaleOrder(models.Model):
|
||||
data += line.prepare_sale_rma_data()
|
||||
return data
|
||||
|
||||
@api.depends("rma_ids.refund_id")
|
||||
def _get_invoiced(self):
|
||||
"""Search for possible RMA refunds and link them to the order. We
|
||||
don't want to link their sale lines as that would unbalance the
|
||||
qtys to invoice wich isn't correct for this case"""
|
||||
super()._get_invoiced()
|
||||
for order in self:
|
||||
refunds = order.sudo().rma_ids.mapped("refund_id")
|
||||
if not refunds:
|
||||
continue
|
||||
order.invoice_ids += refunds
|
||||
order.invoice_count = len(order.invoice_ids)
|
||||
|
||||
|
||||
class SaleOrderLine(models.Model):
|
||||
_inherit = "sale.order.line"
|
||||
@@ -98,7 +111,7 @@ class SaleOrderLine(models.Model):
|
||||
def prepare_sale_rma_data(self):
|
||||
self.ensure_one()
|
||||
product = self.product_id
|
||||
if self.product_id.type != "product":
|
||||
if self.product_id.type not in ["product", "consu"]:
|
||||
return {}
|
||||
moves = self.get_delivery_move()
|
||||
data = []
|
||||
|
||||
45
rma_sale/static/src/js/rma_portal_form.js
Normal file
45
rma_sale/static/src/js/rma_portal_form.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/* Copyright 2021 Tecnativa - David Vidal
|
||||
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */
|
||||
|
||||
odoo.define("rma_sale.animation", function(require) {
|
||||
"use strict";
|
||||
|
||||
var sAnimation = require("website.content.snippets.animation");
|
||||
|
||||
// In the customer portal when a RMA operation is selected show the comments
|
||||
// selector so the user doesn't miss the chance to add his comments
|
||||
sAnimation.registry.rma_operation_portal = sAnimation.Class.extend({
|
||||
selector: ".rma-operation",
|
||||
start: function() {
|
||||
this.id = this.el.name.replace("-operation_id", "");
|
||||
this.$comment = $("#comment-" + this.id);
|
||||
this.$comment_input = $("[name='" + this.id + "-description']");
|
||||
var _this = this;
|
||||
this.$el.on("change", function() {
|
||||
_this._onChangeOperationId();
|
||||
});
|
||||
},
|
||||
_show_comment: function() {
|
||||
if (this.$comment) {
|
||||
this.$comment.removeClass("show");
|
||||
this.$comment.addClass("show");
|
||||
if (this.$comment_input) {
|
||||
this.$comment_input.focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
_hide_comment: function() {
|
||||
if (this.$comment) {
|
||||
this.$comment.removeClass("show");
|
||||
}
|
||||
},
|
||||
_onChangeOperationId: function() {
|
||||
// Toggle comment on or off if an operation is requested
|
||||
if (this.$el && this.$el.val()) {
|
||||
this._show_comment();
|
||||
} else {
|
||||
this._hide_comment();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,10 @@
|
||||
<odoo>
|
||||
<template id="assets_frontend" inherit_id="web.assets_frontend" name="Request RMA">
|
||||
<xpath expr="//link[last()]" position="after">
|
||||
<script
|
||||
type="text/javascript"
|
||||
src="/rma_sale/static/src/js/rma_portal_form.js"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
type="text/scss"
|
||||
|
||||
@@ -67,6 +67,48 @@
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
<t
|
||||
t-set="delivery_addresses"
|
||||
t-value="sale_order.partner_id.commercial_partner_id.mapped('child_ids').filtered(lambda x: x.type in ['contact', 'delivery'])"
|
||||
/>
|
||||
<button
|
||||
class="btn btn-primary btn-block mb8"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#delivery_address_picker"
|
||||
aria-expanded="false"
|
||||
><i
|
||||
class="fa fa-truck"
|
||||
/> Choose a delivery address</button>
|
||||
<div
|
||||
class="col-lg-12 collapse mt8"
|
||||
id="delivery_address_picker"
|
||||
>
|
||||
<div data-toggle="buttons" class="row">
|
||||
<label
|
||||
t-attf-class="card mr4 btn btn-light"
|
||||
t-foreach="delivery_addresses"
|
||||
t-as="address"
|
||||
>
|
||||
<input
|
||||
class="d-none"
|
||||
type="radio"
|
||||
name="partner_shipping_id"
|
||||
t-att-value="address.id"
|
||||
>
|
||||
<strong>
|
||||
<i
|
||||
t-attf-class="text-secondary fa #{address.type == 'delivery' and 'fa-truck' or 'fa-user'}"
|
||||
/>
|
||||
<t t-esc="address.name" />
|
||||
</strong>
|
||||
<pre><h6
|
||||
t-esc="address.contact_address"
|
||||
/></pre>
|
||||
</input>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<t
|
||||
t-set="data_list"
|
||||
t-value="sale_order.get_delivery_rma_data()"
|
||||
@@ -132,18 +174,18 @@
|
||||
</td>
|
||||
<td class="text-left">
|
||||
<span
|
||||
t-esc="data['picking'].name"
|
||||
t-esc="data['picking'] and data['picking'].name"
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
t-attf-name="#{data_index}-picking_id"
|
||||
t-att-value="data['picking'].id"
|
||||
t-att-value="data['picking'] and data['picking'].id"
|
||||
/>
|
||||
</td>
|
||||
<td class="text-left">
|
||||
<select
|
||||
t-attf-name="#{data_index}-operation_id"
|
||||
class="form-control"
|
||||
class="form-control rma-operation"
|
||||
>
|
||||
<option value="">---</option>
|
||||
<t
|
||||
|
||||
@@ -27,6 +27,16 @@ class SaleOrderRmaWizard(models.TransientModel):
|
||||
domain=_domain_location_id,
|
||||
default=lambda r: r.order_id.warehouse_id.rma_loc_id.id,
|
||||
)
|
||||
commercial_partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
related="order_id.partner_id.commercial_partner_id",
|
||||
string="Commercial entity",
|
||||
)
|
||||
partner_shipping_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Shipping Address",
|
||||
help="Will be used to return the goods when the RMA is completed",
|
||||
)
|
||||
|
||||
def create_rma(self, from_portal=None):
|
||||
self.ensure_one()
|
||||
@@ -147,9 +157,13 @@ class SaleOrderLineRmaWizard(models.TransientModel):
|
||||
|
||||
def _prepare_rma_values(self):
|
||||
self.ensure_one()
|
||||
partner_shipping = (
|
||||
self.wizard_id.partner_shipping_id or self.order_id.partner_shipping_id
|
||||
)
|
||||
return {
|
||||
"partner_id": self.order_id.partner_id.id,
|
||||
"partner_invoice_id": self.order_id.partner_invoice_id.id,
|
||||
"partner_shipping_id": partner_shipping.id,
|
||||
"origin": self.order_id.name,
|
||||
"company_id": self.order_id.company_id.id,
|
||||
"location_id": self.wizard_id.location_id.id,
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
</field>
|
||||
</group>
|
||||
<group>
|
||||
<field name="commercial_partner_id" invisible="1" />
|
||||
<field
|
||||
name="partner_shipping_id"
|
||||
domain="[('id', 'child_of', commercial_partner_id)]"
|
||||
/>
|
||||
<field
|
||||
name="location_id"
|
||||
options="{'no_create': True, 'no_open': True}"
|
||||
|
||||
Reference in New Issue
Block a user