mirror of
https://github.com/OCA/rma.git
synced 2025-02-16 17:11:47 +02:00
[MIG] rma: Migration to 16.0
* Standard procedure. * Transfer view groups to nodes. * Adjusted upstream changed field names. * Converted onchanges to computed writable fields. * Replace `Form` by direct dictionary vals in record creation, as they don't handle now properly multiple existing fields in the view, and computed writable improve the compatibility on new values. * Replace domain returned on onchange by static domain in field. * Change maintainer. TT44213
This commit is contained in:
@@ -14,14 +14,14 @@ Return Merchandise Authorization Management
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frma-lightgray.png?logo=github
|
||||
:target: https://github.com/OCA/rma/tree/15.0/rma
|
||||
:target: https://github.com/OCA/rma/tree/16.0/rma
|
||||
:alt: OCA/rma
|
||||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
|
||||
:target: https://translation.odoo-community.org/projects/rma-15-0/rma-15-0-rma
|
||||
:target: https://translation.odoo-community.org/projects/rma-16-0/rma-16-0-rma
|
||||
:alt: Translate me on Weblate
|
||||
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
|
||||
:target: https://runbot.odoo-community.org/runbot/145/15.0
|
||||
:alt: Try me on Runbot
|
||||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
|
||||
:target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/rma&target_branch=16.0
|
||||
:alt: Try me on Runboat
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
@@ -138,7 +138,7 @@ Bug Tracker
|
||||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/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 <https://github.com/OCA/rma/issues/new?body=module:%20rma%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
|
||||
`feedback <https://github.com/OCA/rma/issues/new?body=module:%20rma%0Aversion:%2016.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.
|
||||
|
||||
@@ -184,6 +184,6 @@ Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||
|
||||
|maintainer-ernestotejeda|
|
||||
|
||||
This module is part of the `OCA/rma <https://github.com/OCA/rma/tree/15.0/rma>`_ project on GitHub.
|
||||
This module is part of the `OCA/rma <https://github.com/OCA/rma/tree/16.0/rma>`_ project on GitHub.
|
||||
|
||||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# Copyright 2021-2023 Tecnativa - David Vidal
|
||||
# Copyright 2021-2023 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
{
|
||||
"name": "Return Merchandise Authorization Management",
|
||||
"summary": "Return Merchandise Authorization (RMA)",
|
||||
"version": "15.0.1.1.3",
|
||||
"version": "16.0.1.0.0",
|
||||
"development_status": "Production/Stable",
|
||||
"category": "RMA",
|
||||
"website": "https://github.com/OCA/rma",
|
||||
"author": "Tecnativa, Odoo Community Association (OCA)",
|
||||
"maintainers": ["ernestotejeda"],
|
||||
"maintainers": ["pedrobaeza"],
|
||||
"license": "AGPL-3",
|
||||
"depends": ["stock_account"],
|
||||
"data": [
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8' ?>
|
||||
<odoo>
|
||||
<record id="mail_template_rma_notification" model="mail.template">
|
||||
<field name="email_from">{{object.user_id.email_formatted}}</field>
|
||||
<field name="partner_to">{{object.partner_id.id}}</field>
|
||||
<field
|
||||
name="subject"
|
||||
>{{object.company_id.name}} RMA (Ref {{object.name or 'n/a' }})</field>
|
||||
<field name="report_name">{{(object.name or '')}}</field>
|
||||
<field name="lang">{{object.partner_id.lang}}</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="margin: 0px; padding: 0px;">
|
||||
<p style="margin: 0px; padding: 0px; font-size: 13px;">
|
||||
Dear
|
||||
<t t-out="object.partner_id.name" />
|
||||
<t t-if="object.partner_id.parent_id">
|
||||
<t t-out="object.partner_id.parent_id.name" />
|
||||
</t>
|
||||
<br />
|
||||
<br />
|
||||
Here is the RMA
|
||||
<strong>
|
||||
<t t-out="object.name" />
|
||||
</strong>
|
||||
from
|
||||
<t t-out="object.company_id.name" />
|
||||
.
|
||||
<br />
|
||||
<br />
|
||||
Do not hesitate to contact us if you have any question.
|
||||
</p>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
<record id="mail_template_rma_receipt_notification" model="mail.template">
|
||||
<field name="email_from">{{object.user_id.email_formatted }}</field>
|
||||
<field name="partner_to">{{object.partner_id.id}}</field>
|
||||
<field
|
||||
name="subject"
|
||||
>{{object.company_id.name}} RMA (Ref {{object.name or 'n/a' }}) products received</field>
|
||||
<field name="report_name">{{(object.name or '')}}</field>
|
||||
<field name="lang">{{object.partner_id.lang}}</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="margin: 0px; padding: 0px;">
|
||||
<p style="margin: 0px; padding: 0px; font-size: 13px;">
|
||||
Dear
|
||||
<t t-out="object.partner_id.name" />
|
||||
<t t-if="object.partner_id.parent_id">
|
||||
<t t-out="object.partner_id.parent_id.name" />
|
||||
</t>
|
||||
<br />
|
||||
<br />
|
||||
The products for your RMA
|
||||
<strong>
|
||||
<t t-out="object.name" />
|
||||
</strong>
|
||||
from
|
||||
<t t-out="object.company_id.name" />
|
||||
have been received in our warehouse.
|
||||
<br />
|
||||
<br />
|
||||
Do not hesitate to contact us if you have any question.
|
||||
</p>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
<record id="mail_template_rma_draft_notification" model="mail.template">
|
||||
<field name="email_from">{{object.user_id.email_formatted}}</field>
|
||||
<field name="partner_to">{{object.partner_id.id}}</field>
|
||||
<field
|
||||
name="subject"
|
||||
>{{object.company_id.name}} Your RMA has been succesfully created (Ref {{object.name or 'n/a' }})</field>
|
||||
<field name="report_name">{{(object.name or '')}}</field>
|
||||
<field name="lang">{{object.partner_id.lang}}</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="margin: 0px; padding: 0px;">
|
||||
<p style="margin: 0px; padding: 0px; font-size: 13px;">
|
||||
Dear
|
||||
<t t-out="object.partner_id.name" />
|
||||
<t t-if="object.partner_id.parent_id">
|
||||
<t t-out="object.partner_id.parent_id.name" />
|
||||
</t>
|
||||
<br />
|
||||
<br />
|
||||
You've succesfully placed your RMA
|
||||
<strong>
|
||||
<t t-out="object.name" />
|
||||
</strong>
|
||||
on
|
||||
<t t-out="object.company_id.name" />
|
||||
. Our team will check it and will validate it as soon as possible.
|
||||
<br />
|
||||
<br />
|
||||
Do not hesitate to contact us if you have any question.
|
||||
</p>
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -1,18 +0,0 @@
|
||||
# Copyright 2022 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
from openupgradelib import openupgrade
|
||||
|
||||
|
||||
@openupgrade.migrate()
|
||||
def migrate(env, version):
|
||||
openupgrade.load_data(env.cr, "rma", "migrations/15.0.1.0.0/noupdate_changes.xml")
|
||||
openupgrade.delete_record_translations(
|
||||
env.cr,
|
||||
"rma",
|
||||
[
|
||||
"mail_template_rma_notification",
|
||||
"mail_template_rma_receipt_notification",
|
||||
"mail_template_rma_draft_notification",
|
||||
],
|
||||
)
|
||||
@@ -1,10 +1,11 @@
|
||||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
|
||||
|
||||
class Company(models.Model):
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
def _default_rma_mail_confirmation_template(self):
|
||||
@@ -65,11 +66,12 @@ class Company(models.Model):
|
||||
help="Email sent to the customer when they place " "an RMA from the portal",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
company = super(Company, self).create(vals)
|
||||
company.create_rma_index()
|
||||
return company
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
companies = super().create(vals_list)
|
||||
for company in companies:
|
||||
company.create_rma_index()
|
||||
return companies
|
||||
|
||||
def create_rma_index(self):
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
from collections import Counter
|
||||
@@ -78,6 +79,9 @@ class Rma(models.Model):
|
||||
"locked": [("readonly", True)],
|
||||
"cancelled": [("readonly", True)],
|
||||
},
|
||||
compute="_compute_team_id",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
tag_ids = fields.Many2many(comodel_name="rma.tag", string="Tags")
|
||||
finalization_id = fields.Many2one(
|
||||
@@ -109,19 +113,21 @@ class Rma(models.Model):
|
||||
partner_shipping_id = fields.Many2one(
|
||||
string="Shipping Address",
|
||||
comodel_name="res.partner",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
help="Shipping address for current RMA.",
|
||||
compute="_compute_partner_shipping_id",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
partner_invoice_id = fields.Many2one(
|
||||
string="Invoice Address",
|
||||
comodel_name="res.partner",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
domain=(
|
||||
"['|', ('company_id', '=', False), ('company_id', '='," " company_id)]"
|
||||
),
|
||||
help="Refund address for current RMA.",
|
||||
compute="_compute_partner_invoice_id",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
commercial_partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
@@ -149,28 +155,34 @@ class Rma(models.Model):
|
||||
" ('picking_id', '!=', False)"
|
||||
"]"
|
||||
),
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
compute="_compute_move_id",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
product_id = fields.Many2one(
|
||||
comodel_name="product.product",
|
||||
domain=[("type", "in", ["consu", "product"])],
|
||||
compute="_compute_product_id",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
product_uom_qty = fields.Float(
|
||||
string="Quantity",
|
||||
required=True,
|
||||
default=1.0,
|
||||
digits="Product Unit of Measure",
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
compute="_compute_product_uom_qty",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
product_uom = fields.Many2one(
|
||||
comodel_name="uom.uom",
|
||||
string="UoM",
|
||||
required=True,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
default=lambda self: self.env.ref("uom.product_uom_unit").id,
|
||||
compute="_compute_product_uom",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
procurement_group_id = fields.Many2one(
|
||||
comodel_name="procurement.group",
|
||||
@@ -220,8 +232,9 @@ class Rma(models.Model):
|
||||
location_id = fields.Many2one(
|
||||
comodel_name="stock.location",
|
||||
domain=_domain_location_id,
|
||||
readonly=True,
|
||||
states={"draft": [("readonly", False)]},
|
||||
compute="_compute_location_id",
|
||||
store=True,
|
||||
readonly=False,
|
||||
)
|
||||
warehouse_id = fields.Many2one(
|
||||
comodel_name="stock.warehouse",
|
||||
@@ -463,6 +476,83 @@ class Rma(models.Model):
|
||||
[("rma_loc_id", "parent_of", record.location_id.id)], limit=1
|
||||
)
|
||||
|
||||
@api.depends("user_id")
|
||||
def _compute_team_id(self):
|
||||
self.team_id = False
|
||||
for record in self.filtered("user_id"):
|
||||
record.team_id = (
|
||||
self.env["rma.team"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
"|",
|
||||
("user_id", "=", record.user_id.id),
|
||||
("member_ids", "=", record.user_id.id),
|
||||
"|",
|
||||
("company_id", "=", False),
|
||||
("company_id", "child_of", record.company_id.ids),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
)
|
||||
|
||||
@api.depends("partner_id")
|
||||
def _compute_partner_shipping_id(self):
|
||||
self.partner_shipping_id = False
|
||||
for record in self.filtered("partner_id"):
|
||||
address = record.partner_id.address_get(["delivery"])
|
||||
record.partner_shipping_id = address.get("delivery", False)
|
||||
|
||||
@api.depends("partner_id")
|
||||
def _compute_partner_invoice_id(self):
|
||||
self.partner_invoice_id = False
|
||||
for record in self.filtered("partner_id"):
|
||||
address = record.partner_id.address_get(["invoice"])
|
||||
record.partner_invoice_id = address.get("invoice", False)
|
||||
|
||||
@api.depends("picking_id")
|
||||
def _compute_move_id(self):
|
||||
"""Empty move on picking change, but selecting the move in it if it's single."""
|
||||
self.move_id = False
|
||||
for record in self.filtered("picking_id"):
|
||||
if len(record.picking_id.move_ids) == 1:
|
||||
record.move_id = record.picking_id.move_ids.id
|
||||
|
||||
@api.depends("move_id")
|
||||
def _compute_product_id(self):
|
||||
self.product_id = False
|
||||
for record in self.filtered("move_id"):
|
||||
record.product_id = record.move_id.product_id.id
|
||||
|
||||
@api.depends("move_id")
|
||||
def _compute_product_uom_qty(self):
|
||||
self.product_uom_qty = False
|
||||
for record in self.filtered("move_id"):
|
||||
record.product_uom_qty = record.move_id.product_uom_qty
|
||||
|
||||
@api.depends("move_id", "product_id")
|
||||
def _compute_product_uom(self):
|
||||
for record in self:
|
||||
if record.move_id:
|
||||
record.product_uom = record.move_id.product_uom.id
|
||||
elif record.product_id:
|
||||
record.product_uom = record.product_id.uom_id
|
||||
else:
|
||||
record.product_uom = False
|
||||
|
||||
@api.depends("picking_id", "product_id", "company_id")
|
||||
def _compute_location_id(self):
|
||||
for record in self:
|
||||
if record.picking_id:
|
||||
warehouse = record.picking_id.picking_type_id.warehouse_id
|
||||
record.location_id = warehouse.rma_loc_id.id
|
||||
elif not record.location_id:
|
||||
company = record.company_id or self.env.company
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", company.id)], limit=1
|
||||
)
|
||||
record.location_id = warehouse.rma_loc_id.id
|
||||
|
||||
def _compute_access_url(self):
|
||||
for record in self:
|
||||
record.access_url = "/my/rmas/{}".format(record.id)
|
||||
@@ -483,76 +573,6 @@ class Rma(models.Model):
|
||||
rma = self.filtered(lambda r: r.state not in ["draft", "cancelled"])
|
||||
rma._ensure_required_fields()
|
||||
|
||||
# onchange methods (@api.onchange)
|
||||
@api.onchange("user_id")
|
||||
def _onchange_user_id(self):
|
||||
if self.user_id:
|
||||
self.team_id = (
|
||||
self.env["rma.team"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
"|",
|
||||
("user_id", "=", self.user_id.id),
|
||||
("member_ids", "=", self.user_id.id),
|
||||
"|",
|
||||
("company_id", "=", False),
|
||||
("company_id", "child_of", self.company_id.ids),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.team_id = False
|
||||
|
||||
@api.onchange("partner_id")
|
||||
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", "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):
|
||||
location = False
|
||||
if self.picking_id:
|
||||
warehouse = self.picking_id.picking_type_id.warehouse_id
|
||||
location = warehouse.rma_loc_id.id
|
||||
self.location_id = location
|
||||
self.move_id = False
|
||||
self.product_id = False
|
||||
|
||||
@api.onchange("move_id")
|
||||
def _onchange_move_id(self):
|
||||
if self.move_id:
|
||||
self.product_id = self.move_id.product_id
|
||||
self.product_uom_qty = self.move_id.product_uom_qty
|
||||
self.product_uom = self.move_id.product_uom
|
||||
|
||||
@api.onchange("product_id")
|
||||
def _onchange_product_id(self):
|
||||
if self.product_id:
|
||||
# Set UoM
|
||||
if not self.product_uom or self.product_id.uom_id.id != self.product_uom.id:
|
||||
self.product_uom = self.product_id.uom_id
|
||||
# Set stock location (location_id)
|
||||
user = self.env.user
|
||||
if (
|
||||
not user.has_group("stock.group_stock_multi_locations")
|
||||
and not self.location_id
|
||||
):
|
||||
# If this condition is True, it is because a picking is not set
|
||||
company = self.company_id or self.env.company
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("company_id", "=", company.id)], limit=1
|
||||
)
|
||||
self.location_id = warehouse.rma_loc_id.id
|
||||
|
||||
# CRUD methods (ORM overrides)
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
@@ -677,41 +697,25 @@ class Rma(models.Model):
|
||||
group_dict[key] |= record
|
||||
for rmas in group_dict.values():
|
||||
origin = ", ".join(rmas.mapped("name"))
|
||||
invoice_form = Form(
|
||||
self.env["account.move"]
|
||||
.sudo()
|
||||
.with_context(
|
||||
default_move_type="out_refund",
|
||||
company_id=rmas[0].company_id.id,
|
||||
),
|
||||
"account.view_move_form",
|
||||
)
|
||||
rmas[0]._prepare_refund(invoice_form, origin)
|
||||
refund = invoice_form.save()
|
||||
refund_vals = rmas[0]._prepare_refund_vals(origin)
|
||||
for rma in rmas:
|
||||
# For each iteration the Form is edited, a new invoice line
|
||||
# is added and then saved. This is to generate the other
|
||||
# lines of the accounting entry and to specify the associated
|
||||
# RMA to that new invoice line.
|
||||
invoice_form = Form(refund)
|
||||
with invoice_form.invoice_line_ids.new() as line_form:
|
||||
rma._prepare_refund_line(line_form)
|
||||
refund = invoice_form.save()
|
||||
line = refund.invoice_line_ids.filtered(lambda r: not r.rma_id)
|
||||
line.rma_id = rma.id
|
||||
rma.write(
|
||||
refund_vals["invoice_line_ids"].append(
|
||||
(0, 0, rma._prepare_refund_line_vals())
|
||||
)
|
||||
refund = self.env["account.move"].sudo().create(refund_vals)
|
||||
refund.with_user(self.env.uid).message_post_with_view(
|
||||
"mail.message_origin_link",
|
||||
values={"self": refund, "origin": rmas},
|
||||
subtype_id=self.env.ref("mail.mt_note").id,
|
||||
)
|
||||
for line in refund.invoice_line_ids:
|
||||
line.rma_id.write(
|
||||
{
|
||||
"refund_line_id": line.id,
|
||||
"refund_id": refund.id,
|
||||
"state": "refunded",
|
||||
}
|
||||
)
|
||||
refund.invoice_origin = origin
|
||||
refund.with_user(self.env.uid).message_post_with_view(
|
||||
"mail.message_origin_link",
|
||||
values={"self": refund, "origin": rmas},
|
||||
subtype_id=self.env.ref("mail.mt_note").id,
|
||||
)
|
||||
|
||||
def action_replace(self):
|
||||
"""Invoked when 'Replace' button in rma form view is clicked."""
|
||||
@@ -880,7 +884,6 @@ class Rma(models.Model):
|
||||
rma._check_required_after_draft
|
||||
rma.action_confirm
|
||||
"""
|
||||
ir_translation = self.env["ir.translation"]
|
||||
required = [
|
||||
"partner_id",
|
||||
"partner_shipping_id",
|
||||
@@ -891,7 +894,17 @@ class Rma(models.Model):
|
||||
for record in self:
|
||||
desc = ""
|
||||
for field in filter(lambda item: not record[item], required):
|
||||
desc += "\n%s" % ir_translation.get_field_string("rma")[field]
|
||||
field_record = (
|
||||
self.env["ir.model.fields"]
|
||||
.sudo()
|
||||
.search(
|
||||
[
|
||||
("model_id.model", "=", record._name),
|
||||
("name", "=", field),
|
||||
]
|
||||
)
|
||||
)
|
||||
desc += f"\n{field_record.field_description}"
|
||||
if desc:
|
||||
raise ValidationError(_("Required field(s):%s") % desc)
|
||||
|
||||
@@ -982,9 +995,9 @@ class Rma(models.Model):
|
||||
active_model="stock.picking",
|
||||
)
|
||||
)
|
||||
if self.location_id:
|
||||
stock_return_picking_form.location_id = self.location_id
|
||||
return_wizard = stock_return_picking_form.save()
|
||||
if self.location_id:
|
||||
return_wizard.location_id = self.location_id
|
||||
return_wizard.product_return_moves.filtered(
|
||||
lambda r: r.move_id != self.move_id
|
||||
).unlink()
|
||||
@@ -992,8 +1005,8 @@ class Rma(models.Model):
|
||||
return_line.update(
|
||||
{
|
||||
"quantity": self.product_uom_qty,
|
||||
# The to_refund field is now True by default, which isn't right in the RMA
|
||||
# creation context.
|
||||
# The to_refund field is now True by default, which isn't right in the
|
||||
# RMA creation context
|
||||
"to_refund": False,
|
||||
}
|
||||
)
|
||||
@@ -1005,20 +1018,13 @@ class Rma(models.Model):
|
||||
picking_id = picking_action["res_id"]
|
||||
picking = self.env["stock.picking"].browse(picking_id)
|
||||
picking.origin = "{} ({})".format(self.name, picking.origin)
|
||||
move = picking.move_lines
|
||||
move = picking.move_ids
|
||||
move.priority = self.priority
|
||||
return move
|
||||
|
||||
def _create_receptions_from_product(self):
|
||||
self.ensure_one()
|
||||
picking_form = Form(
|
||||
recordp=self.env["stock.picking"].with_context(
|
||||
default_picking_type_id=self.warehouse_id.rma_in_type_id.id
|
||||
),
|
||||
view="stock.view_picking_form",
|
||||
)
|
||||
self._prepare_picking(picking_form)
|
||||
picking = picking_form.save()
|
||||
picking = self.env["stock.picking"].create(self._prepare_picking_vals())
|
||||
picking.action_confirm()
|
||||
picking.action_assign()
|
||||
picking.message_post_with_view(
|
||||
@@ -1026,17 +1032,33 @@ class Rma(models.Model):
|
||||
values={"self": picking, "origin": self},
|
||||
subtype_id=self.env.ref("mail.mt_note").id,
|
||||
)
|
||||
return picking.move_lines
|
||||
return picking.move_ids
|
||||
|
||||
def _prepare_picking(self, picking_form):
|
||||
picking_form.origin = self.name
|
||||
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
|
||||
move_form.product_uom_qty = self.product_uom_qty
|
||||
move_form.product_uom = self.product_uom
|
||||
def _prepare_picking_vals(self):
|
||||
return {
|
||||
"picking_type_id": self.warehouse_id.rma_in_type_id.id,
|
||||
"origin": self.name,
|
||||
"partner_id": self.partner_shipping_id.id,
|
||||
"location_id": self.partner_shipping_id.property_stock_customer.id,
|
||||
"location_dest_id": self.location_id.id,
|
||||
"move_ids": [
|
||||
(
|
||||
0,
|
||||
0,
|
||||
{
|
||||
"product_id": self.product_id.id,
|
||||
# same text as origin move or product text in partner lang
|
||||
"name": self.move_id.name
|
||||
or self.product_id.with_context(
|
||||
lang=self.partner_id.lang or "en_US"
|
||||
).display_name,
|
||||
"location_id": self.partner_shipping_id.property_stock_customer.id,
|
||||
"location_dest_id": self.location_id.id,
|
||||
"product_uom_qty": self.product_uom_qty,
|
||||
},
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
# Extract business methods
|
||||
def extract_quantity(self, qty, uom):
|
||||
@@ -1074,7 +1096,7 @@ class Rma(models.Model):
|
||||
return extracted_rma
|
||||
|
||||
# Refund business methods
|
||||
def _prepare_refund(self, invoice_form, origin):
|
||||
def _prepare_refund_vals(self, origin=False):
|
||||
"""Hook method for preparing the refund Form.
|
||||
|
||||
This method could be override in order to add new custom field
|
||||
@@ -1084,11 +1106,16 @@ class Rma(models.Model):
|
||||
rma.action_refund
|
||||
"""
|
||||
self.ensure_one()
|
||||
invoice_form.partner_id = self.partner_invoice_id
|
||||
# Avoid set partner default value
|
||||
invoice_form.invoice_payment_term_id = self.env["account.payment.term"]
|
||||
return {
|
||||
"move_type": "out_refund",
|
||||
"company_id": self.company_id.id,
|
||||
"partner_id": self.partner_invoice_id.id,
|
||||
"invoice_payment_term_id": False,
|
||||
"invoice_origin": origin,
|
||||
"invoice_line_ids": [],
|
||||
}
|
||||
|
||||
def _prepare_refund_line(self, line_form):
|
||||
def _prepare_refund_line_vals(self):
|
||||
"""Hook method for preparing a refund line Form.
|
||||
|
||||
This method could be override in order to add new custom field
|
||||
@@ -1098,31 +1125,13 @@ class Rma(models.Model):
|
||||
rma.action_refund
|
||||
"""
|
||||
self.ensure_one()
|
||||
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 {}
|
||||
return {
|
||||
"product_id": self.product_id.id,
|
||||
"quantity": self.product_uom_qty,
|
||||
"product_uom_id": self.product_uom.id,
|
||||
"price_unit": self.product_id.lst_price,
|
||||
"rma_id": self.id,
|
||||
}
|
||||
|
||||
# Returning business methods
|
||||
def create_return(self, scheduled_date, qty=None, uom=None):
|
||||
@@ -1148,32 +1157,13 @@ class Rma(models.Model):
|
||||
grouped_rmas = rmas_to_return
|
||||
for rmas in grouped_rmas:
|
||||
origin = ", ".join(rmas.mapped("name"))
|
||||
rma_out_type = rmas[0].warehouse_id.rma_out_type_id
|
||||
picking_form = Form(
|
||||
recordp=self.env["stock.picking"].with_context(
|
||||
default_picking_type_id=rma_out_type.id
|
||||
),
|
||||
view="stock.view_picking_form",
|
||||
)
|
||||
rmas[0]._prepare_returning_picking(picking_form, origin)
|
||||
picking = picking_form.save()
|
||||
picking_vals = rmas[0]._prepare_returning_picking_vals(origin)
|
||||
for rma in rmas:
|
||||
with picking_form.move_ids_without_package.new() as move_form:
|
||||
rma._prepare_returning_move(move_form, scheduled_date, qty, uom)
|
||||
# rma_id is not present in the form view, so we need to get
|
||||
# the 'values to save' to add the rma id and use the
|
||||
# create method intead of save the form.
|
||||
picking_vals = picking_form._values_to_save(all_fields=True)
|
||||
move_vals = picking_vals["move_ids_without_package"][-1][2]
|
||||
move_vals.update(
|
||||
picking_id=picking.id,
|
||||
rma_id=rma.id,
|
||||
move_orig_ids=[(4, rma.reception_move_id.id)],
|
||||
company_id=picking.company_id.id,
|
||||
picking_vals["move_ids"].append(
|
||||
(0, 0, rma._prepare_returning_move_vals(scheduled_date, qty, uom))
|
||||
)
|
||||
if "product_qty" in move_vals:
|
||||
move_vals.pop("product_qty")
|
||||
self.env["stock.move"].sudo().create(move_vals)
|
||||
picking = self.env["stock.picking"].create(picking_vals)
|
||||
for rma in rmas:
|
||||
rma.message_post(
|
||||
body=_(
|
||||
'Return: <a href="#" data-oe-model="stock.picking" '
|
||||
@@ -1190,18 +1180,34 @@ class Rma(models.Model):
|
||||
)
|
||||
rmas_to_return.write({"state": "waiting_return"})
|
||||
|
||||
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_shipping_id
|
||||
def _prepare_returning_picking_vals(self, origin=None):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"picking_type_id": self.warehouse_id.rma_out_type_id.id,
|
||||
"location_id": self.location_id.id,
|
||||
"location_dest_id": self.reception_move_id.location_id.id,
|
||||
"origin": origin or self.name,
|
||||
"partner_id": self.partner_shipping_id.id,
|
||||
"company_id": self.company_id.id,
|
||||
"move_ids": [],
|
||||
}
|
||||
|
||||
def _prepare_returning_move(
|
||||
self, move_form, scheduled_date, quantity=None, uom=None
|
||||
):
|
||||
move_form.product_id = self.product_id
|
||||
move_form.product_uom_qty = quantity or self.product_uom_qty
|
||||
move_form.product_uom = uom or self.product_uom
|
||||
move_form.date = scheduled_date
|
||||
def _prepare_returning_move_vals(self, scheduled_date, quantity=None, uom=None):
|
||||
self.ensure_one()
|
||||
return {
|
||||
"product_id": self.product_id.id,
|
||||
"name": self.product_id.with_context(
|
||||
lang=self.partner_shipping_id.lang or "en_US"
|
||||
).display_name,
|
||||
"product_uom_qty": quantity or self.product_uom_qty,
|
||||
"product_uom": uom and uom.id or self.product_uom.id,
|
||||
"location_id": self.location_id.id,
|
||||
"location_dest_id": self.reception_move_id.location_id.id,
|
||||
"date": scheduled_date,
|
||||
"rma_id": self.id,
|
||||
"move_orig_ids": [(4, self.reception_move_id.id)],
|
||||
"company_id": self.company_id.id,
|
||||
}
|
||||
|
||||
# Replacing business methods
|
||||
def create_replace(self, scheduled_date, warehouse, product, qty, uom):
|
||||
|
||||
@@ -14,23 +14,24 @@ class StockPicking(models.Model):
|
||||
|
||||
def _compute_rma_count(self):
|
||||
for rec in self:
|
||||
rec.rma_count = len(rec.move_lines.mapped("rma_ids"))
|
||||
rec.rma_count = len(rec.move_ids.mapped("rma_ids"))
|
||||
|
||||
def copy(self, default=None):
|
||||
self.ensure_one()
|
||||
if self.env.context.get("set_rma_picking_type"):
|
||||
location_dest_id = default["location_dest_id"]
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("rma_loc_id", "parent_of", location_dest_id)], limit=1
|
||||
)
|
||||
if warehouse:
|
||||
default["picking_type_id"] = warehouse.rma_in_type_id.id
|
||||
location_dest_id = default.get("location_dest_id")
|
||||
if location_dest_id:
|
||||
warehouse = self.env["stock.warehouse"].search(
|
||||
[("rma_loc_id", "parent_of", location_dest_id)], limit=1
|
||||
)
|
||||
if warehouse:
|
||||
default["picking_type_id"] = warehouse.rma_in_type_id.id
|
||||
return super().copy(default)
|
||||
|
||||
def action_view_rma(self):
|
||||
self.ensure_one()
|
||||
action = self.sudo().env.ref("rma.rma_action").read()[0]
|
||||
rma = self.move_lines.mapped("rma_ids")
|
||||
rma = self.move_ids.mapped("rma_ids")
|
||||
if len(rma) == 1:
|
||||
action.update(
|
||||
res_id=rma.id,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<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 0.15.1: http://docutils.sourceforge.net/" />
|
||||
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
|
||||
<title>Return Merchandise Authorization Management</title>
|
||||
<style type="text/css">
|
||||
|
||||
@@ -367,7 +367,7 @@ ul.auto-toc {
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/rma/tree/15.0/rma"><img alt="OCA/rma" src="https://img.shields.io/badge/github-OCA%2Frma-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/rma-15-0/rma-15-0-rma"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/145/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
|
||||
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/rma/tree/16.0/rma"><img alt="OCA/rma" src="https://img.shields.io/badge/github-OCA%2Frma-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/rma-16-0/rma-16-0-rma"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runboat.odoo-community.org/webui/builds.html?repo=OCA/rma&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>This module allows you to manage <a class="reference external" href="https://en.wikipedia.org/wiki/Return_merchandise_authorization">Return Merchandise Authorization (RMA)</a>.
|
||||
RMA documents can be created from scratch, from a delivery order or from
|
||||
an incoming email. Product receptions and returning delivery operations
|
||||
@@ -494,7 +494,7 @@ the product of that move is.</li>
|
||||
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/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 smashing it by providing a detailed and welcomed
|
||||
<a class="reference external" href="https://github.com/OCA/rma/issues/new?body=module:%20rma%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
|
||||
<a class="reference external" href="https://github.com/OCA/rma/issues/new?body=module:%20rma%0Aversion:%2016.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">
|
||||
@@ -528,7 +528,7 @@ mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.</p>
|
||||
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
|
||||
<p><a class="reference external" href="https://github.com/ernestotejeda"><img alt="ernestotejeda" src="https://github.com/ernestotejeda.png?size=40px" /></a></p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/rma/tree/15.0/rma">OCA/rma</a> project on GitHub.</p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/rma/tree/16.0/rma">OCA/rma</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,16 @@ class TestRma(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.env = cls.env(
|
||||
context=dict(
|
||||
cls.env.context,
|
||||
mail_create_nolog=True,
|
||||
mail_create_nosubscribe=True,
|
||||
mail_notrack=True,
|
||||
no_reset_password=True,
|
||||
tracking_disable=True,
|
||||
)
|
||||
)
|
||||
cls.user_rma = new_test_user(
|
||||
cls.env,
|
||||
login="user_rma",
|
||||
@@ -24,18 +34,11 @@ class TestRma(TransactionCase):
|
||||
cls.product = cls.product_product.create(
|
||||
{"name": "Product test 1", "type": "product"}
|
||||
)
|
||||
account_type = cls.env["account.account.type"].create(
|
||||
{
|
||||
"name": "RCV type",
|
||||
"type": "receivable",
|
||||
"internal_group": "income",
|
||||
}
|
||||
)
|
||||
cls.account_receiv = cls.env["account.account"].create(
|
||||
{
|
||||
"name": "Receivable",
|
||||
"code": "RCV00",
|
||||
"user_type_id": account_type.id,
|
||||
"account_type": "asset_receivable",
|
||||
"reconcile": True,
|
||||
}
|
||||
)
|
||||
@@ -73,16 +76,16 @@ class TestRma(TransactionCase):
|
||||
cls.env.company.rma_return_grouping = True
|
||||
|
||||
def _create_rma(self, partner=None, product=None, qty=None, location=None):
|
||||
rma_form = Form(self.env["rma"])
|
||||
vals = {}
|
||||
if partner:
|
||||
rma_form.partner_id = partner
|
||||
vals["partner_id"] = partner.id
|
||||
if product:
|
||||
rma_form.product_id = product
|
||||
vals["product_id"] = product.id
|
||||
if qty:
|
||||
rma_form.product_uom_qty = qty
|
||||
vals["product_uom_qty"] = qty
|
||||
if location:
|
||||
rma_form.location_id = location
|
||||
return rma_form.save()
|
||||
vals["location_id"] = location.id
|
||||
return self.env["rma"].create(vals)
|
||||
|
||||
def _create_confirm_receive(
|
||||
self, partner=None, product=None, qty=None, location=None
|
||||
@@ -93,25 +96,6 @@ class TestRma(TransactionCase):
|
||||
rma.reception_move_id.picking_id._action_done()
|
||||
return rma
|
||||
|
||||
def _test_readonly_fields(self, rma):
|
||||
with Form(rma) as rma_form:
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.partner_id = self.env["res.partner"]
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.partner_invoice_id = self.env["res.partner"]
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.picking_id = self.env["stock.picking"]
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.move_id = self.env["stock.move"]
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.product_id = self.env["product.product"]
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.product_uom_qty = 0
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.product_uom = self.env["uom.uom"]
|
||||
with self.assertRaises(AssertionError):
|
||||
rma_form.location_id = self.env["stock.location"]
|
||||
|
||||
def _create_delivery(self):
|
||||
picking_type = self.env["stock.picking.type"].search(
|
||||
[
|
||||
@@ -139,18 +123,18 @@ class TestRma(TransactionCase):
|
||||
move.product_uom_qty = 20
|
||||
picking = picking_form.save()
|
||||
picking.action_confirm()
|
||||
for move in picking.move_lines:
|
||||
for move in picking.move_ids:
|
||||
move.quantity_done = move.product_uom_qty
|
||||
picking.button_validate()
|
||||
return picking
|
||||
|
||||
|
||||
class TestRmaCase(TestRma):
|
||||
def test_onchange(self):
|
||||
rma_form = Form(self.env["rma"])
|
||||
def test_computed(self):
|
||||
# If partner changes, the invoice address is set
|
||||
rma_form.partner_id = self.partner
|
||||
self.assertEqual(rma_form.partner_invoice_id, self.partner_invoice)
|
||||
rma = self.env["rma"].new()
|
||||
rma.partner_id = self.partner
|
||||
self.assertEqual(rma.partner_invoice_id, self.partner_invoice)
|
||||
# If origin move changes, the product is set
|
||||
uom_ten = self.env["uom.uom"].create(
|
||||
{
|
||||
@@ -182,21 +166,17 @@ class TestRmaCase(TestRma):
|
||||
with picking_form.move_ids_without_package.new() as move:
|
||||
move.product_id = product_2
|
||||
move.product_uom_qty = 15
|
||||
move.product_uom = uom_ten
|
||||
picking = picking_form.save()
|
||||
picking._action_done()
|
||||
rma_form.picking_id = picking
|
||||
rma_form.move_id = picking.move_lines
|
||||
self.assertEqual(rma_form.product_id, product_2)
|
||||
self.assertEqual(rma_form.product_uom_qty, 15)
|
||||
self.assertEqual(rma_form.product_uom, uom_ten)
|
||||
rma.picking_id = picking
|
||||
rma.move_id = picking.move_ids
|
||||
self.assertEqual(rma.product_id, product_2)
|
||||
self.assertEqual(rma.product_uom_qty, 15)
|
||||
self.assertEqual(rma.product_uom, uom_ten)
|
||||
# If product changes, unit of measure changes
|
||||
rma_form.picking_id = self.env["stock.picking"]
|
||||
rma_form.product_id = self.product
|
||||
self.assertEqual(rma_form.product_id, self.product)
|
||||
self.assertEqual(rma_form.product_uom_qty, 15)
|
||||
self.assertNotEqual(rma_form.product_uom, uom_ten)
|
||||
self.assertEqual(rma_form.product_uom, self.product.uom_id)
|
||||
rma.move_id = False
|
||||
rma.product_id = self.product
|
||||
self.assertEqual(rma.product_uom, self.product.uom_id)
|
||||
|
||||
def test_ensure_required_fields_on_confirm(self):
|
||||
rma = self._create_rma()
|
||||
@@ -204,17 +184,14 @@ class TestRmaCase(TestRma):
|
||||
rma.action_confirm()
|
||||
self.assertEqual(
|
||||
e.exception.args[0],
|
||||
"Required field(s):\nCustomer\nShipping Address\nInvoice Address\n"
|
||||
"Product\nLocation",
|
||||
"Required field(s):\nCustomer\nShipping Address\nInvoice Address\nProduct",
|
||||
)
|
||||
with Form(rma) as rma_form:
|
||||
rma_form.partner_id = self.partner
|
||||
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\nLocation")
|
||||
with Form(rma) as rma_form:
|
||||
rma_form.product_id = self.product
|
||||
rma_form.location_id = self.rma_loc
|
||||
self.assertEqual(e.exception.args[0], "Required field(s):\nProduct")
|
||||
rma.product_id = self.product.id
|
||||
rma.location_id = self.rma_loc.id
|
||||
rma.action_confirm()
|
||||
self.assertEqual(rma.state, "confirmed")
|
||||
|
||||
@@ -226,7 +203,6 @@ class TestRmaCase(TestRma):
|
||||
self.assertEqual(rma.reception_move_id.product_uom_qty, 10)
|
||||
self.assertEqual(rma.reception_move_id.product_uom, rma.product_uom)
|
||||
self.assertEqual(rma.state, "confirmed")
|
||||
self._test_readonly_fields(rma)
|
||||
rma.reception_move_id.quantity_done = 9
|
||||
with self.assertRaises(ValidationError):
|
||||
rma.reception_move_id.picking_id._action_done()
|
||||
@@ -235,14 +211,12 @@ class TestRmaCase(TestRma):
|
||||
self.assertEqual(rma.reception_move_id.picking_id.state, "done")
|
||||
self.assertEqual(rma.reception_move_id.quantity_done, 10)
|
||||
self.assertEqual(rma.state, "received")
|
||||
self._test_readonly_fields(rma)
|
||||
|
||||
def test_cancel(self):
|
||||
# cancel a draft RMA
|
||||
rma = self._create_rma(self.partner, self.product)
|
||||
rma.action_cancel()
|
||||
self.assertEqual(rma.state, "cancelled")
|
||||
self._test_readonly_fields(rma)
|
||||
# cancel a confirmed RMA
|
||||
rma = self._create_rma(self.partner, self.product, 10, self.rma_loc)
|
||||
rma.action_confirm()
|
||||
@@ -304,7 +278,6 @@ class TestRmaCase(TestRma):
|
||||
self.assertFalse(rma.can_be_refunded)
|
||||
self.assertFalse(rma.can_be_returned)
|
||||
self.assertFalse(rma.can_be_replaced)
|
||||
self._test_readonly_fields(rma)
|
||||
|
||||
def test_mass_refund(self):
|
||||
# Create, confirm and receive rma_1
|
||||
@@ -403,7 +376,7 @@ class TestRmaCase(TestRma):
|
||||
delivery_form.product_uom_qty = 2
|
||||
delivery_wizard = delivery_form.save()
|
||||
delivery_wizard.action_deliver()
|
||||
self.assertEqual(len(rma.delivery_move_ids.picking_id.move_lines), 1)
|
||||
self.assertEqual(len(rma.delivery_move_ids.picking_id.move_ids), 1)
|
||||
self.assertEqual(rma.delivery_move_ids.product_id, product_2)
|
||||
self.assertEqual(rma.delivery_move_ids.product_uom_qty, 2)
|
||||
self.assertTrue(rma.delivery_move_ids.picking_id.state, "waiting")
|
||||
@@ -459,7 +432,6 @@ class TestRmaCase(TestRma):
|
||||
# Despite being in 'replaced' state,
|
||||
# RMAs can still perform replacements.
|
||||
self.assertTrue(rma.can_be_replaced)
|
||||
self._test_readonly_fields(rma)
|
||||
|
||||
def test_return_to_customer(self):
|
||||
# Create, confirm and receive an RMA
|
||||
@@ -475,7 +447,7 @@ class TestRmaCase(TestRma):
|
||||
delivery_wizard = delivery_form.save()
|
||||
delivery_wizard.action_deliver()
|
||||
picking = rma.delivery_move_ids.picking_id
|
||||
self.assertEqual(len(picking.move_lines), 1)
|
||||
self.assertEqual(len(picking.move_ids), 1)
|
||||
self.assertEqual(rma.delivery_move_ids.product_id, self.product)
|
||||
self.assertEqual(rma.delivery_move_ids.product_uom_qty, 2)
|
||||
self.assertTrue(picking.state, "waiting")
|
||||
@@ -527,7 +499,6 @@ class TestRmaCase(TestRma):
|
||||
self.assertFalse(rma.can_be_refunded)
|
||||
self.assertFalse(rma.can_be_returned)
|
||||
self.assertFalse(rma.can_be_replaced)
|
||||
self._test_readonly_fields(rma)
|
||||
|
||||
def test_finish_rma(self):
|
||||
# Create, confirm and receive an RMA
|
||||
@@ -590,19 +561,19 @@ class TestRmaCase(TestRma):
|
||||
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
|
||||
# line of picking_1
|
||||
self.assertEqual(len(pick_1.move_lines), 3)
|
||||
self.assertEqual(len(pick_1.move_ids), 3)
|
||||
self.assertEqual(
|
||||
pick_1.move_lines.mapped("rma_id"),
|
||||
pick_1.move_ids.mapped("rma_id"),
|
||||
(rma_1 | rma_2 | rma_3),
|
||||
)
|
||||
self.assertEqual(
|
||||
(rma_1 | rma_2 | rma_3).mapped("delivery_move_ids"),
|
||||
pick_1.move_lines,
|
||||
pick_1.move_ids,
|
||||
)
|
||||
# rma_4 is linked with the unique move of pick_2
|
||||
self.assertEqual(len(pick_2.move_lines), 1)
|
||||
self.assertEqual(pick_2.move_lines.rma_id, rma_4)
|
||||
self.assertEqual(rma_4.delivery_move_ids, pick_2.move_lines)
|
||||
self.assertEqual(len(pick_2.move_ids), 1)
|
||||
self.assertEqual(pick_2.move_ids.rma_id, rma_4)
|
||||
self.assertEqual(rma_4.delivery_move_ids, pick_2.move_ids)
|
||||
# Assert product and quantities are propagated correctly
|
||||
for rma in all_rmas:
|
||||
self.assertEqual(rma.product_id, rma.delivery_move_ids.product_id)
|
||||
@@ -660,14 +631,14 @@ class TestRmaCase(TestRma):
|
||||
return_wizard = stock_return_picking_form.save()
|
||||
picking_action = return_wizard.create_returns()
|
||||
# Each origin move is linked to a different RMA
|
||||
origin_moves = origin_delivery.move_lines
|
||||
origin_moves = origin_delivery.move_ids
|
||||
self.assertTrue(origin_moves[0].rma_ids)
|
||||
self.assertTrue(origin_moves[1].rma_ids)
|
||||
rmas = origin_moves.mapped("rma_ids")
|
||||
self.assertEqual(rmas.mapped("state"), ["confirmed"] * 2)
|
||||
# Each reception move is linked one of the generated RMAs
|
||||
reception = self.env["stock.picking"].browse(picking_action["res_id"])
|
||||
reception_moves = reception.move_lines
|
||||
reception_moves = reception.move_ids
|
||||
self.assertTrue(reception_moves[0].rma_receiver_ids)
|
||||
self.assertTrue(reception_moves[1].rma_receiver_ids)
|
||||
self.assertEqual(reception_moves.mapped("rma_receiver_ids"), rmas)
|
||||
@@ -682,7 +653,7 @@ class TestRmaCase(TestRma):
|
||||
rma_form = Form(self.env["rma"])
|
||||
rma_form.partner_id = self.partner
|
||||
rma_form.picking_id = origin_delivery
|
||||
rma_form.move_id = origin_delivery.move_lines.filtered(
|
||||
rma_form.move_id = origin_delivery.move_ids.filtered(
|
||||
lambda r: r.product_id == self.product
|
||||
)
|
||||
rma = rma_form.save()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="view_partner_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.form</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form" />
|
||||
<field name="groups_id" eval="[(4, ref('rma.rma_group_user_own'))]" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box">
|
||||
<button
|
||||
@@ -15,6 +15,7 @@
|
||||
class="oe_stat_button"
|
||||
icon="fa-reply"
|
||||
attrs="{'invisible': [('rma_count', '=', 0)]}"
|
||||
groups="rma.rma_group_user_own"
|
||||
>
|
||||
<field name="rma_count" widget="statinfo" string="RMA" />
|
||||
</button>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<group>
|
||||
<field name="name" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
<field name="company_id" invisible="1" />
|
||||
<field name="active" invisible="1" />
|
||||
</group>
|
||||
</sheet>
|
||||
@@ -47,6 +48,7 @@
|
||||
<tree>
|
||||
<field name="name" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
<field name="company_id" invisible="1" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<field name="name" />
|
||||
<field name="user_id" />
|
||||
<field name="company_id" groups="base.group_multi_company" />
|
||||
<field name="company_id" invisible="1" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -33,6 +34,7 @@
|
||||
options="{'no_create': True}"
|
||||
groups="base.group_multi_company"
|
||||
/>
|
||||
<field name="company_id" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="rma_view_search" model="ir.ui.view">
|
||||
@@ -239,10 +240,19 @@
|
||||
name="partner_id"
|
||||
widget="res_partner_many2one"
|
||||
context="{'search_default_customer':1, 'show_address': 1, 'show_vat': True}"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
options="{'always_reload': True}"
|
||||
/>
|
||||
<field name="partner_shipping_id" />
|
||||
<field name="partner_invoice_id" />
|
||||
<field
|
||||
name="partner_shipping_id"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
force_save="1"
|
||||
/>
|
||||
<field
|
||||
name="partner_invoice_id"
|
||||
attrs="{'readonly': [('state', 'not in', ['draft', 'confirmed', 'received'])]}"
|
||||
force_save="1"
|
||||
/>
|
||||
<field name="picking_id" options="{'no_create': True}" />
|
||||
<field
|
||||
name="move_id"
|
||||
@@ -258,12 +268,19 @@
|
||||
<field name="uom_category_id" invisible="1" />
|
||||
<label for="product_uom_qty" />
|
||||
<div class="o_row">
|
||||
<field name="product_uom_qty" />
|
||||
<field
|
||||
name="product_uom_qty"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
force_save="1"
|
||||
/>
|
||||
<field
|
||||
name="product_uom"
|
||||
groups="uom.group_uom"
|
||||
domain="[('category_id', '=', uom_category_id)]"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
force_save="1"
|
||||
/>
|
||||
<field name="product_uom" invisible="1" />
|
||||
</div>
|
||||
<field
|
||||
name="delivered_qty"
|
||||
@@ -291,6 +308,7 @@
|
||||
options="{'no_create': True}"
|
||||
groups="base.group_multi_company"
|
||||
/>
|
||||
<field name="company_id" invisible="1" />
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
@@ -302,7 +320,9 @@
|
||||
name="location_id"
|
||||
options="{'no_create': True, 'no_open': True}"
|
||||
groups="stock.group_stock_multi_locations"
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
/>
|
||||
<field name="location_id" invisible="1" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="deadline" />
|
||||
@@ -341,10 +361,6 @@
|
||||
<record id="rma_finalization_form" model="ir.ui.view">
|
||||
<field name="model">rma</field>
|
||||
<field name="inherit_id" ref="rma.rma_view_form" />
|
||||
<field
|
||||
name="groups_id"
|
||||
eval="[(4, ref('rma.group_rma_manual_finalization'))]"
|
||||
/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath
|
||||
expr="//form//header//button[@name='action_cancel']"
|
||||
@@ -356,6 +372,7 @@
|
||||
name="action_finish"
|
||||
class="btn-primary"
|
||||
attrs="{'invisible': [('can_be_finished', '=', False)]}"
|
||||
groups="rma.group_rma_manual_finalization"
|
||||
/>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
<field name="name">stock.picking.form</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form" />
|
||||
<field name="groups_id" eval="[(4, ref('rma.rma_group_user_own'))]" />
|
||||
<field name="arch" type="xml">
|
||||
<div name="button_box">
|
||||
<button
|
||||
@@ -15,6 +14,7 @@
|
||||
class="oe_stat_button"
|
||||
icon="fa-reply"
|
||||
attrs="{'invisible': [('rma_count', '=', 0)]}"
|
||||
groups="rma.rma_group_user_own"
|
||||
>
|
||||
<field name="rma_count" widget="statinfo" string="RMA" />
|
||||
</button>
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
<field name="name">Stock Warehouse Inherit MRP</field>
|
||||
<field name="model">stock.warehouse</field>
|
||||
<field name="inherit_id" ref="stock.view_warehouse" />
|
||||
<field name="groups_id" eval="[(4, ref('rma.rma_group_user_own'))]" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='wh_output_stock_loc_id']/..">
|
||||
<field name="rma_loc_id" />
|
||||
<field name="rma_loc_id" groups="rma.rma_group_user_own" />
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='out_type_id']/..">
|
||||
<field name="rma_in_type_id" />
|
||||
<field name="rma_out_type_id" />
|
||||
<field name="rma_in_type_id" groups="rma.rma_group_user_own" />
|
||||
<field name="rma_out_type_id" groups="rma.rma_group_user_own" />
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="rma_redelivery_wizard_view_form" model="ir.ui.view">
|
||||
@@ -43,6 +44,7 @@
|
||||
attrs="{'required': [('rma_count', '=', 1)]}"
|
||||
domain="[('category_id', '=', uom_category_id)]"
|
||||
/>
|
||||
<field name="product_uom" invisible="1" />
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="rma_split_wizard_view_form2" model="ir.ui.view">
|
||||
@@ -13,6 +14,7 @@
|
||||
<div class="o_row">
|
||||
<field name="product_uom_qty" />
|
||||
<field name="product_uom" groups="uom.group_uom" />
|
||||
<field name="product_uom" invisible="1" />
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
@@ -10,39 +10,43 @@ class ReturnPicking(models.TransientModel):
|
||||
|
||||
create_rma = fields.Boolean(string="Create RMAs")
|
||||
picking_type_code = fields.Selection(related="picking_id.picking_type_id.code")
|
||||
rma_location_ids = fields.Many2many(
|
||||
comodel_name="stock.location", compute="_compute_rma_location_id"
|
||||
)
|
||||
# Expand domain for RMAs
|
||||
location_id = fields.Many2one(
|
||||
domain="create_rma and [('id', 'child_of', rma_location_ids)]"
|
||||
"or "
|
||||
"['|', ('id', '=', original_location_id), '|', '&', "
|
||||
"('return_location', '=', True), ('company_id', '=', False), '&', "
|
||||
"('return_location', '=', True), ('company_id', '=', company_id)]"
|
||||
)
|
||||
|
||||
@api.depends("picking_id")
|
||||
def _compute_rma_location_id(self):
|
||||
for record in self:
|
||||
record.rma_location_ids = (
|
||||
self.env["stock.warehouse"]
|
||||
.search([("company_id", "=", record.picking_id.company_id.id)])
|
||||
.rma_loc_id
|
||||
)
|
||||
|
||||
@api.onchange("create_rma")
|
||||
def _onchange_create_rma(self):
|
||||
if self.create_rma:
|
||||
warehouse = self.picking_id.picking_type_id.warehouse_id
|
||||
self.location_id = warehouse.rma_loc_id.id
|
||||
rma_loc = warehouse.search(
|
||||
[("company_id", "=", self.picking_id.company_id.id)]
|
||||
).mapped("rma_loc_id")
|
||||
rma_loc_domain = [("id", "child_of", rma_loc.ids)]
|
||||
# We want to avoid setting the return move `to_refund` as it will change
|
||||
# the delivered quantities in the sale and set them to invoice.
|
||||
self.product_return_moves.to_refund = False
|
||||
else:
|
||||
# If self.create_rma is not True, the value of the location and
|
||||
# the location domain will be the same as assigned by default.
|
||||
# If self.create_rma is not True, the value of the location will be the
|
||||
# same as assigned by default
|
||||
location_id = self.picking_id.location_id.id
|
||||
return_picking_type = self.picking_id.picking_type_id.return_picking_type_id
|
||||
if return_picking_type.default_location_dest_id.return_location:
|
||||
location_id = return_picking_type.default_location_dest_id.id
|
||||
self.location_id = location_id
|
||||
rma_loc_domain = [
|
||||
"|",
|
||||
("id", "=", self.picking_id.location_id.id),
|
||||
"|",
|
||||
"&",
|
||||
("return_location", "=", True),
|
||||
("company_id", "=", False),
|
||||
"&",
|
||||
("return_location", "=", True),
|
||||
("company_id", "=", self.picking_id.company_id.id),
|
||||
]
|
||||
return {"domain": {"location_id": rma_loc_domain}}
|
||||
|
||||
def create_returns(self):
|
||||
"""Override create_returns method for creating one or more
|
||||
@@ -67,7 +71,7 @@ class ReturnPicking(models.TransientModel):
|
||||
returned_picking = self.env["stock.picking"].browse(res["res_id"])
|
||||
vals_list = [
|
||||
move._prepare_return_rma_vals(self.picking_id)
|
||||
for move in returned_picking.move_lines
|
||||
for move in returned_picking.move_ids
|
||||
]
|
||||
self.env["rma"].create(vals_list)
|
||||
return res
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
Copyright 2023 Tecnativa - Pedro M. Baeza
|
||||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
|
||||
<odoo>
|
||||
<record id="view_stock_return_picking_form" model="ir.ui.view">
|
||||
@@ -7,19 +8,17 @@
|
||||
<field name="model">stock.return.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_stock_return_picking_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath
|
||||
expr="//group[.//field[@name='product_return_moves']]"
|
||||
position="before"
|
||||
>
|
||||
<field name="product_return_moves" position="after">
|
||||
<group name="group_rma">
|
||||
<field
|
||||
name="create_rma"
|
||||
attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"
|
||||
/>
|
||||
<field name="rma_location_ids" invisible="1" />
|
||||
<field name="picking_id" invisible="1" />
|
||||
<field name="picking_type_code" invisible="1" />
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user