mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-20 06:52:02 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
15
odoo-bringout-oca-rma-rma/rma/models/__init__.py
Normal file
15
odoo-bringout-oca-rma-rma/rma/models/__init__.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import account_move
|
||||
from . import rma
|
||||
from . import rma_finalization
|
||||
from . import rma_operation
|
||||
from . import rma_tag
|
||||
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
|
||||
from . import stock_picking
|
||||
from . import stock_warehouse
|
||||
55
odoo-bringout-oca-rma-rma/rma/models/account_move.py
Normal file
55
odoo-bringout-oca-rma-rma/rma/models/account_move.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
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"
|
||||
)
|
||||
return (
|
||||
self.sudo()
|
||||
.mapped("invoice_line_ids")
|
||||
.filtered(
|
||||
lambda r: (
|
||||
r.rma_id
|
||||
and float_compare(r.quantity, r.rma_id.product_uom_qty, precision)
|
||||
< 0
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def action_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 "
|
||||
"less than the quantity specified in its linked RMA."
|
||||
)
|
||||
)
|
||||
return super().action_post()
|
||||
|
||||
def unlink(self):
|
||||
rma = self.mapped("invoice_line_ids.rma_id")
|
||||
rma.write({"state": "received"})
|
||||
return super().unlink()
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
rma_id = fields.Many2one(
|
||||
comodel_name="rma",
|
||||
string="RMA",
|
||||
)
|
||||
89
odoo-bringout-oca-rma-rma/rma/models/res_company.py
Normal file
89
odoo-bringout-oca-rma-rma/rma/models/res_company.py
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# 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 ResCompany(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
|
||||
|
||||
def _default_rma_mail_receipt_template(self):
|
||||
try:
|
||||
return self.env.ref("rma.mail_template_rma_receipt_notification").id
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def _default_rma_mail_draft_template(self):
|
||||
try:
|
||||
return self.env.ref("rma.mail_template_rma_draft_notification").id
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
rma_return_grouping = fields.Boolean(
|
||||
string="Group RMA returns by customer address and warehouse",
|
||||
default=True,
|
||||
)
|
||||
send_rma_confirmation = fields.Boolean(
|
||||
string="Send RMA Confirmation",
|
||||
help="When the delivery is confirmed, send a confirmation email "
|
||||
"to the customer.",
|
||||
)
|
||||
send_rma_receipt_confirmation = fields.Boolean(
|
||||
string="Send RMA Receipt Confirmation",
|
||||
help="When the RMA receipt is confirmed, send a confirmation email "
|
||||
"to the customer.",
|
||||
)
|
||||
send_rma_draft_confirmation = fields.Boolean(
|
||||
string="Send RMA draft Confirmation",
|
||||
help="When a customer places an RMA, send a notification with it",
|
||||
)
|
||||
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.",
|
||||
)
|
||||
rma_mail_receipt_confirmation_template_id = fields.Many2one(
|
||||
comodel_name="mail.template",
|
||||
string="Email Template receipt confirmation for RMA",
|
||||
domain="[('model', '=', 'rma')]",
|
||||
default=_default_rma_mail_receipt_template,
|
||||
help="Email sent to the customer once the RMA products are received.",
|
||||
)
|
||||
rma_mail_draft_confirmation_template_id = fields.Many2one(
|
||||
comodel_name="mail.template",
|
||||
string="Email Template draft notification for RMA",
|
||||
domain="[('model', '=', 'rma')]",
|
||||
default=_default_rma_mail_draft_template,
|
||||
help="Email sent to the customer when they place " "an RMA from the portal",
|
||||
)
|
||||
|
||||
@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 (
|
||||
self.env["ir.sequence"]
|
||||
.sudo()
|
||||
.create(
|
||||
{
|
||||
"name": _("RMA Code"),
|
||||
"prefix": "RMA",
|
||||
"code": "rma",
|
||||
"padding": 4,
|
||||
"company_id": self.id,
|
||||
}
|
||||
)
|
||||
)
|
||||
41
odoo-bringout-oca-rma-rma/rma/models/res_config_settings.py
Normal file
41
odoo-bringout-oca-rma-rma/rma/models/res_config_settings.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# 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"
|
||||
|
||||
group_rma_manual_finalization = fields.Boolean(
|
||||
string="Finish RMA manually choosing a reason",
|
||||
help="Allow to finish an RMA without returning back a product or refunding",
|
||||
implied_group="rma.group_rma_manual_finalization",
|
||||
)
|
||||
rma_return_grouping = fields.Boolean(
|
||||
related="company_id.rma_return_grouping",
|
||||
readonly=False,
|
||||
)
|
||||
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,
|
||||
)
|
||||
send_rma_receipt_confirmation = fields.Boolean(
|
||||
related="company_id.send_rma_receipt_confirmation",
|
||||
readonly=False,
|
||||
)
|
||||
rma_mail_receipt_confirmation_template_id = fields.Many2one(
|
||||
related="company_id.rma_mail_receipt_confirmation_template_id",
|
||||
readonly=False,
|
||||
)
|
||||
send_rma_draft_confirmation = fields.Boolean(
|
||||
related="company_id.send_rma_draft_confirmation",
|
||||
readonly=False,
|
||||
)
|
||||
rma_mail_draft_confirmation_template_id = fields.Many2one(
|
||||
related="company_id.rma_mail_draft_confirmation_template_id",
|
||||
readonly=False,
|
||||
)
|
||||
41
odoo-bringout-oca-rma-rma/rma/models/res_partner.py
Normal file
41
odoo-bringout-oca-rma-rma/rma/models/res_partner.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = "res.partner"
|
||||
|
||||
rma_ids = fields.One2many(
|
||||
comodel_name="rma",
|
||||
inverse_name="partner_id",
|
||||
string="RMAs",
|
||||
)
|
||||
rma_count = fields.Integer(
|
||||
string="RMA count",
|
||||
compute="_compute_rma_count",
|
||||
)
|
||||
|
||||
def _compute_rma_count(self):
|
||||
rma_data = self.env["rma"].read_group(
|
||||
[("partner_id", "in", self.ids)], ["partner_id"], ["partner_id"]
|
||||
)
|
||||
mapped_data = {r["partner_id"][0]: r["partner_id_count"] for r in rma_data}
|
||||
for record in self:
|
||||
record.rma_count = mapped_data.get(record.id, 0)
|
||||
|
||||
def action_view_rma(self):
|
||||
self.ensure_one()
|
||||
action = self.sudo().env.ref("rma.rma_action").read()[0]
|
||||
rma = self.rma_ids
|
||||
if len(rma) == 1:
|
||||
action.update(
|
||||
res_id=rma.id,
|
||||
view_mode="form",
|
||||
view_id=False,
|
||||
views=False,
|
||||
)
|
||||
else:
|
||||
action["domain"] = [("partner_id", "in", self.ids)]
|
||||
return action
|
||||
14
odoo-bringout-oca-rma-rma/rma/models/res_users.py
Normal file
14
odoo-bringout-oca-rma-rma/rma/models/res_users.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResUsers(models.Model):
|
||||
_inherit = "res.users"
|
||||
|
||||
rma_team_id = fields.Many2one(
|
||||
comodel_name="rma.team",
|
||||
string="RMA Team",
|
||||
help="RMA Team the user is member of.",
|
||||
)
|
||||
1581
odoo-bringout-oca-rma-rma/rma/models/rma.py
Normal file
1581
odoo-bringout-oca-rma-rma/rma/models/rma.py
Normal file
File diff suppressed because it is too large
Load diff
26
odoo-bringout-oca-rma-rma/rma/models/rma_finalization.py
Normal file
26
odoo-bringout-oca-rma-rma/rma/models/rma_finalization.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright 2022 Tecnativa - David Vidal
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class RmaFinalization(models.Model):
|
||||
_description = "RMA Finalization Reason"
|
||||
_name = "rma.finalization"
|
||||
_order = "name"
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(
|
||||
string="Reason Name",
|
||||
required=True,
|
||||
translate=True,
|
||||
copy=False,
|
||||
)
|
||||
company_id = fields.Many2one(comodel_name="res.company")
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
"name_company_uniq",
|
||||
"unique (name, company_id)",
|
||||
"Finalization name already exists !",
|
||||
),
|
||||
]
|
||||
163
odoo-bringout-oca-rma-rma/rma/models/rma_operation.py
Normal file
163
odoo-bringout-oca-rma-rma/rma/models/rma_operation.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from ast import literal_eval
|
||||
from collections import defaultdict
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.osv.expression import AND
|
||||
|
||||
PROCESSED_STATES = ["received", "refunded", "replaced", "finished"]
|
||||
AWAITING_ACTION_STATES = ["waiting_return", "waiting_replacement", "confirmed"]
|
||||
|
||||
|
||||
class RmaOperation(models.Model):
|
||||
_name = "rma.operation"
|
||||
_description = "RMA requested operation"
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
name = fields.Char(required=True, translate=True)
|
||||
color = fields.Integer()
|
||||
count_rma_draft = fields.Integer(compute="_compute_count_rma")
|
||||
count_rma_awaiting_action = fields.Integer(compute="_compute_count_rma")
|
||||
count_rma_processed = fields.Integer(compute="_compute_count_rma")
|
||||
action_create_receipt = fields.Selection(
|
||||
[
|
||||
("manual_on_confirm", "Manually on Confirm"),
|
||||
("automatic_on_confirm", "Automatically on Confirm"),
|
||||
],
|
||||
string="Create Receipt",
|
||||
default="automatic_on_confirm",
|
||||
help="Define how the receipt action should be handled.",
|
||||
)
|
||||
different_return_product = fields.Boolean(
|
||||
help="If checked, allows the return of a product different from the one "
|
||||
"originally ordered. Used if the delivery is created automatically",
|
||||
)
|
||||
auto_confirm_reception = fields.Boolean(
|
||||
help="Enable this option to automatically confirm the reception when the RMA is"
|
||||
" confirmed."
|
||||
)
|
||||
action_create_delivery = fields.Selection(
|
||||
[
|
||||
("manual_on_confirm", "Manually on Confirm"),
|
||||
("automatic_on_confirm", "Automatically on Confirm"),
|
||||
("manual_after_receipt", "Manually After Receipt"),
|
||||
("automatic_after_receipt", "Automatically After Receipt"),
|
||||
],
|
||||
string="Delivery Action",
|
||||
help="Define how the delivery action should be handled.",
|
||||
default="manual_after_receipt",
|
||||
)
|
||||
action_create_refund = fields.Selection(
|
||||
[
|
||||
("manual_on_confirm", "Manually on Confirm"),
|
||||
("automatic_on_confirm", "Automatically on Confirm"),
|
||||
("manual_after_receipt", "Manually After Receipt"),
|
||||
("automatic_after_receipt", "Automatically After Receipt"),
|
||||
("update_quantity", "Update Quantities"),
|
||||
],
|
||||
string="Refund Action",
|
||||
default="manual_after_receipt",
|
||||
help="Define how the refund action should be handled.",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique (name)", "That operation name already exists !"),
|
||||
]
|
||||
|
||||
@api.model
|
||||
def _get_rma_draft_domain(self):
|
||||
return [("state", "=", "draft")]
|
||||
|
||||
@api.model
|
||||
def _get_rma_awaiting_action_domain(self):
|
||||
return [("state", "in", AWAITING_ACTION_STATES)]
|
||||
|
||||
@api.model
|
||||
def _get_rma_processed_domain(self):
|
||||
return [("state", "in", PROCESSED_STATES)]
|
||||
|
||||
def _compute_count_rma(self):
|
||||
self.update(
|
||||
{
|
||||
"count_rma_draft": 0,
|
||||
"count_rma_processed": 0,
|
||||
"count_rma_awaiting_action": 0,
|
||||
}
|
||||
)
|
||||
state_by_op = defaultdict(int)
|
||||
for group in self.env["rma"].read_group(
|
||||
AND([[("operation_id", "!=", False)]]),
|
||||
groupby=["operation_id", "state"],
|
||||
fields=["id"],
|
||||
lazy=False,
|
||||
):
|
||||
operation_id = group.get("operation_id")[0]
|
||||
state = group.get("state")
|
||||
count = group.get("__count")
|
||||
if state == "draft":
|
||||
state_by_op[(operation_id, "count_rma_draft")] += count
|
||||
if state in PROCESSED_STATES:
|
||||
state_by_op[(operation_id, "count_rma_processed")] += count
|
||||
if state in AWAITING_ACTION_STATES:
|
||||
state_by_op[(operation_id, "count_rma_awaiting_action")] += count
|
||||
for (operation_id, field), count in state_by_op.items():
|
||||
self.browse(operation_id).update({field: count})
|
||||
|
||||
def _get_action(self, name, domain):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("rma.rma_action")
|
||||
action["display_name"] = name
|
||||
context = {
|
||||
"search_default_operation_id": [self.id],
|
||||
"default_operation_id": self.id,
|
||||
}
|
||||
action_context = literal_eval(action["context"])
|
||||
context = {**action_context, **context}
|
||||
action["context"] = context
|
||||
action["domain"] = domain
|
||||
return action
|
||||
|
||||
def get_action_rma_tree_draft(self):
|
||||
self.ensure_one()
|
||||
name = self.display_name + ": " + _("Draft")
|
||||
return self._get_action(
|
||||
name,
|
||||
domain=AND(
|
||||
[
|
||||
[("operation_id", "=", self.id)],
|
||||
self._get_rma_draft_domain(),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
def get_action_rma_tree_awaiting_action(self):
|
||||
self.ensure_one()
|
||||
name = self.display_name + ": " + _("Awaiting Action")
|
||||
return self._get_action(
|
||||
name,
|
||||
domain=AND(
|
||||
[
|
||||
[("operation_id", "=", self.id)],
|
||||
self._get_rma_awaiting_action_domain(),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
def get_action_rma_tree_processed(self):
|
||||
self.ensure_one()
|
||||
name = self.display_name + ": " + _("Processed")
|
||||
return self._get_action(
|
||||
name,
|
||||
domain=AND(
|
||||
[
|
||||
[("operation_id", "=", self.id)],
|
||||
self._get_rma_processed_domain(),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
def get_action_all_rma(self):
|
||||
self.ensure_one()
|
||||
name = self.display_name
|
||||
return self._get_action(name, domain=[("operation_id", "=", self.id)])
|
||||
30
odoo-bringout-oca-rma-rma/rma/models/rma_tag.py
Normal file
30
odoo-bringout-oca-rma-rma/rma/models/rma_tag.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Copyright 2021 Tecnativa - David Vidal
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class RmaTag(models.Model):
|
||||
_description = "RMA Tags"
|
||||
_name = "rma.tag"
|
||||
_order = "name"
|
||||
|
||||
active = fields.Boolean(
|
||||
default=True,
|
||||
help="The active field allows you to hide the category without " "removing it.",
|
||||
)
|
||||
name = fields.Char(
|
||||
string="Tag Name",
|
||||
required=True,
|
||||
translate=True,
|
||||
copy=False,
|
||||
)
|
||||
is_public = fields.Boolean(
|
||||
string="Public Tag",
|
||||
help="The tag is visible in the portal view",
|
||||
)
|
||||
color = fields.Integer(string="Color Index")
|
||||
rma_ids = fields.Many2many(comodel_name="rma")
|
||||
|
||||
_sql_constraints = [
|
||||
("name_uniq", "unique (name)", "Tag name already exists !"),
|
||||
]
|
||||
64
odoo-bringout-oca-rma-rma/rma/models/rma_team.py
Normal file
64
odoo-bringout-oca-rma-rma/rma/models/rma_team.py
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
import ast
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
||||
|
||||
class RmaTeam(models.Model):
|
||||
_name = "rma.team"
|
||||
_inherit = ["mail.alias.mixin", "mail.thread"]
|
||||
_description = "RMA Team"
|
||||
_order = "sequence, name"
|
||||
|
||||
sequence = fields.Integer()
|
||||
name = fields.Char(
|
||||
required=True,
|
||||
translate=True,
|
||||
)
|
||||
active = fields.Boolean(
|
||||
default=True,
|
||||
help="If the active field is set to false, it will allow you "
|
||||
"to hide the RMA Team without removing it.",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
default=lambda self: self.env.company,
|
||||
)
|
||||
user_id = fields.Many2one(
|
||||
comodel_name="res.users",
|
||||
string="Team Leader",
|
||||
domain=[("share", "=", False)],
|
||||
default=lambda self: self.env.user,
|
||||
)
|
||||
member_ids = fields.One2many(
|
||||
comodel_name="res.users",
|
||||
inverse_name="rma_team_id",
|
||||
string="Team Members",
|
||||
)
|
||||
|
||||
def copy(self, default=None):
|
||||
self.ensure_one()
|
||||
if default is None:
|
||||
default = {}
|
||||
if not default.get("name"):
|
||||
default["name"] = _("%s (copy)") % self.name
|
||||
team = super().copy(default)
|
||||
for follower in self.message_follower_ids:
|
||||
team.message_subscribe(
|
||||
partner_ids=follower.partner_id.ids,
|
||||
subtype_ids=follower.subtype_ids.ids,
|
||||
)
|
||||
return team
|
||||
|
||||
def _alias_get_creation_values(self):
|
||||
values = super()._alias_get_creation_values()
|
||||
values["alias_model_id"] = self.env.ref("rma.model_rma").id
|
||||
if self.id:
|
||||
values["alias_defaults"] = defaults = ast.literal_eval(
|
||||
self.alias_defaults or "{}"
|
||||
)
|
||||
defaults["team_id"] = self.id
|
||||
return values
|
||||
138
odoo-bringout-oca-rma-rma/rma/models/stock_move.py
Normal file
138
odoo-bringout-oca-rma-rma/rma/models/stock_move.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
_inherit = "stock.move"
|
||||
|
||||
# RMAs that were created from the delivery move
|
||||
rma_ids = fields.One2many(
|
||||
comodel_name="rma",
|
||||
inverse_name="move_id",
|
||||
string="RMAs",
|
||||
copy=False,
|
||||
)
|
||||
# RMAs linked to the incoming movement from client
|
||||
rma_receiver_ids = fields.One2many(
|
||||
comodel_name="rma",
|
||||
inverse_name="reception_move_id",
|
||||
string="RMA receivers",
|
||||
copy=False,
|
||||
)
|
||||
# RMA that creates the out move
|
||||
rma_id = fields.Many2one(
|
||||
comodel_name="rma", string="RMA return", copy=False, index=True
|
||||
)
|
||||
|
||||
def unlink(self):
|
||||
# A stock user could have no RMA permissions, so the ids wouldn't
|
||||
# be accessible due to record rules.
|
||||
rma_receiver = self.sudo().rma_receiver_ids
|
||||
rma = self.sudo().rma_id
|
||||
res = super().unlink()
|
||||
rma_receiver.filtered(lambda x: x.state != "cancelled").write(
|
||||
{"state": "draft"}
|
||||
)
|
||||
rma.update_received_state()
|
||||
rma.update_replaced_state()
|
||||
return res
|
||||
|
||||
def _action_cancel(self):
|
||||
res = super()._action_cancel()
|
||||
# A stock user could have no RMA permissions, so the ids wouldn't
|
||||
# be accessible due to record rules.
|
||||
cancelled_moves = self.filtered(lambda r: r.state == "cancel").sudo()
|
||||
cancelled_moves.mapped("rma_receiver_ids").write({"state": "draft"})
|
||||
cancelled_moves.mapped("rma_id").update_received_state()
|
||||
cancelled_moves.mapped("rma_id").update_replaced_state()
|
||||
return res
|
||||
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
"""Avoids to validate stock.move with less quantity than the
|
||||
quantity in the linked receiver RMA. It also set the appropriated
|
||||
linked RMA to 'received' or 'delivered'.
|
||||
"""
|
||||
for move in self.filtered(lambda r: r.state not in ("done", "cancel")):
|
||||
rma_receiver = move.sudo().rma_receiver_ids
|
||||
qty_prec = self.env["decimal.precision"].precision_get(
|
||||
"Product Unit of Measure"
|
||||
)
|
||||
if (
|
||||
rma_receiver
|
||||
and float_compare(
|
||||
move.quantity_done,
|
||||
rma_receiver.product_uom_qty,
|
||||
precision_digits=qty_prec,
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The quantity done for the product '%(id)s' must "
|
||||
"be equal to its initial demand because the "
|
||||
"stock move is linked to an RMA (%(name)s)."
|
||||
)
|
||||
% (
|
||||
{
|
||||
"id": move.product_id.name,
|
||||
"name": move.rma_receiver_ids.name,
|
||||
}
|
||||
)
|
||||
)
|
||||
res = super()._action_done(cancel_backorder=cancel_backorder)
|
||||
move_done = self.filtered(lambda r: r.state == "done").sudo()
|
||||
# Set RMAs as received. We sudo so we can grant the operation even
|
||||
# if the stock user has no RMA permissions.
|
||||
to_be_received = (
|
||||
move_done.sudo()
|
||||
.mapped("rma_receiver_ids")
|
||||
.filtered(lambda r: r.state == "confirmed")
|
||||
)
|
||||
to_be_received.update_received_state_on_reception()
|
||||
# Set RMAs as delivered
|
||||
move_done.mapped("rma_id").update_replaced_state()
|
||||
move_done.mapped("rma_id").update_returned_state()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _prepare_merge_moves_distinct_fields(self):
|
||||
"""The main use is that launched delivery RMAs doesn't merge
|
||||
two moves if they are linked to a different RMAs.
|
||||
"""
|
||||
return super()._prepare_merge_moves_distinct_fields() + [
|
||||
"rma_id",
|
||||
"rma_receiver_ids",
|
||||
]
|
||||
|
||||
def _prepare_move_split_vals(self, qty):
|
||||
"""Intended to the backport of picking linked to RMAs propagates the
|
||||
RMA link id.
|
||||
"""
|
||||
res = super()._prepare_move_split_vals(qty)
|
||||
res["rma_id"] = self.sudo().rma_id.id
|
||||
return res
|
||||
|
||||
def _prepare_procurement_values(self):
|
||||
res = super()._prepare_procurement_values()
|
||||
if self.rma_id:
|
||||
res["rma_id"] = self.rma_id.id
|
||||
return res
|
||||
|
||||
|
||||
class StockRule(models.Model):
|
||||
_inherit = "stock.rule"
|
||||
|
||||
def _get_custom_move_fields(self):
|
||||
move_fields = super()._get_custom_move_fields()
|
||||
move_fields += [
|
||||
"rma_id",
|
||||
"origin_returned_move_id",
|
||||
"move_orig_ids",
|
||||
"rma_receiver_ids",
|
||||
]
|
||||
return move_fields
|
||||
44
odoo-bringout-oca-rma-rma/rma/models/stock_picking.py
Normal file
44
odoo-bringout-oca-rma-rma/rma/models/stock_picking.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
rma_count = fields.Integer(
|
||||
string="RMA count",
|
||||
compute="_compute_rma_count",
|
||||
)
|
||||
|
||||
def _compute_rma_count(self):
|
||||
for rec in self:
|
||||
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.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.env["ir.actions.act_window"]._for_xml_id("rma.rma_action")
|
||||
rma = self.move_ids.rma_ids
|
||||
if len(rma) == 1:
|
||||
action.update(
|
||||
res_id=rma.id,
|
||||
view_mode="form",
|
||||
view_id=False,
|
||||
views=False,
|
||||
)
|
||||
else:
|
||||
action["domain"] = [("id", "in", rma.ids)]
|
||||
return action
|
||||
216
odoo-bringout-oca-rma-rma/rma/models/stock_warehouse.py
Normal file
216
odoo-bringout-oca-rma-rma/rma/models/stock_warehouse.py
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
# Copyright 2020 Tecnativa - Ernesto Tejeda
|
||||
# Copyright 2023 Michael Tietz (MT Software) <mtietz@mt-software.de>
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
||||
|
||||
class StockWarehouse(models.Model):
|
||||
_inherit = "stock.warehouse"
|
||||
|
||||
# This is a strategic field used to create an rma location
|
||||
# and rma operation types in existing warehouses when
|
||||
# installing this module.
|
||||
rma = fields.Boolean(
|
||||
"RMA",
|
||||
default=True,
|
||||
help="RMA related products can be stored in this warehouse.",
|
||||
)
|
||||
rma_in_type_id = fields.Many2one(
|
||||
comodel_name="stock.picking.type",
|
||||
string="RMA In Type",
|
||||
)
|
||||
rma_out_type_id = fields.Many2one(
|
||||
comodel_name="stock.picking.type",
|
||||
string="RMA Out Type",
|
||||
)
|
||||
rma_loc_id = fields.Many2one(
|
||||
comodel_name="stock.location",
|
||||
string="RMA Location",
|
||||
)
|
||||
rma_in_route_id = fields.Many2one("stock.route", "RMA in Route")
|
||||
rma_out_route_id = fields.Many2one("stock.route", "RMA out Route")
|
||||
rma_out_replace_route_id = fields.Many2one("stock.route", "RMA out Replace Route")
|
||||
|
||||
def _get_rma_location_values(self, vals, code=False):
|
||||
"""this method is intended to be used by 'create' method
|
||||
to create a new RMA location to be linked to a new warehouse.
|
||||
"""
|
||||
company_id = vals.get(
|
||||
"company_id", self.default_get(["company_id"])["company_id"]
|
||||
)
|
||||
code = vals.get("code") or code or ""
|
||||
code = code.replace(" ", "").upper()
|
||||
view_location_id = vals.get("view_location_id")
|
||||
view_location = (
|
||||
view_location_id
|
||||
and self.view_location_id.browse(view_location_id)
|
||||
or self.view_location_id
|
||||
)
|
||||
return {
|
||||
"name": view_location.name,
|
||||
"active": True,
|
||||
"return_location": True,
|
||||
"usage": "internal",
|
||||
"company_id": company_id,
|
||||
"location_id": self.env.ref("rma.stock_location_rma").id,
|
||||
"barcode": self._valid_barcode(code + "-RMA", company_id),
|
||||
}
|
||||
|
||||
def _get_locations_values(self, vals, code=False):
|
||||
res = super()._get_locations_values(vals, code)
|
||||
res["rma_loc_id"] = self._get_rma_location_values(vals, code)
|
||||
return res
|
||||
|
||||
def _get_sequence_values(self, name=False, code=False):
|
||||
values = super()._get_sequence_values(name=name, code=code)
|
||||
values.update(
|
||||
{
|
||||
"rma_in_type_id": {
|
||||
"name": self.name + " " + _("Sequence RMA in"),
|
||||
"prefix": self.code + "/RMA/IN/",
|
||||
"padding": 5,
|
||||
"company_id": self.company_id.id,
|
||||
},
|
||||
"rma_out_type_id": {
|
||||
"name": self.name + " " + _("Sequence RMA out"),
|
||||
"prefix": self.code + "/RMA/OUT/",
|
||||
"padding": 5,
|
||||
"company_id": self.company_id.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
return values
|
||||
|
||||
def _update_name_and_code(self, new_name=False, new_code=False):
|
||||
res = super()._update_name_and_code(new_name, new_code)
|
||||
for warehouse in self:
|
||||
sequence_data = warehouse._get_sequence_values()
|
||||
warehouse.rma_in_type_id.sequence_id.write(sequence_data["rma_in_type_id"])
|
||||
warehouse.rma_out_type_id.sequence_id.write(
|
||||
sequence_data["rma_out_type_id"]
|
||||
)
|
||||
return res
|
||||
|
||||
def _get_picking_type_create_values(self, max_sequence):
|
||||
data, next_sequence = super()._get_picking_type_create_values(max_sequence)
|
||||
data.update(
|
||||
{
|
||||
"rma_in_type_id": {
|
||||
"name": _("RMA Receipts"),
|
||||
"code": "incoming",
|
||||
"use_create_lots": False,
|
||||
"use_existing_lots": True,
|
||||
"default_location_src_id": False,
|
||||
"default_location_dest_id": self.rma_loc_id.id,
|
||||
"sequence": max_sequence + 1,
|
||||
"sequence_code": "RMA/IN",
|
||||
"company_id": self.company_id.id,
|
||||
},
|
||||
"rma_out_type_id": {
|
||||
"name": _("RMA Delivery Orders"),
|
||||
"code": "outgoing",
|
||||
"use_create_lots": False,
|
||||
"use_existing_lots": True,
|
||||
"default_location_src_id": self.rma_loc_id.id,
|
||||
"default_location_dest_id": False,
|
||||
"sequence": max_sequence + 2,
|
||||
"sequence_code": "RMA/OUT",
|
||||
"company_id": self.company_id.id,
|
||||
},
|
||||
}
|
||||
)
|
||||
return data, max_sequence + 3
|
||||
|
||||
def _get_picking_type_update_values(self):
|
||||
data = super()._get_picking_type_update_values()
|
||||
picking_types = {
|
||||
"rma_in_type_id": {"default_location_dest_id": self.rma_loc_id.id},
|
||||
"rma_out_type_id": {"default_location_src_id": self.rma_loc_id.id},
|
||||
}
|
||||
if self.env.context.get("rma_post_init_hook"):
|
||||
return picking_types
|
||||
data.update(picking_types)
|
||||
return data
|
||||
|
||||
def _create_or_update_sequences_and_picking_types(self):
|
||||
data = super()._create_or_update_sequences_and_picking_types()
|
||||
stock_picking_type = self.env["stock.picking.type"]
|
||||
if "out_type_id" in data:
|
||||
rma_out_type = stock_picking_type.browse(data["rma_out_type_id"])
|
||||
rma_out_type.write(
|
||||
{"return_picking_type_id": data.get("rma_in_type_id", False)}
|
||||
)
|
||||
if "rma_in_type_id" in data:
|
||||
rma_in_type = stock_picking_type.browse(data["rma_in_type_id"])
|
||||
rma_in_type.write(
|
||||
{"return_picking_type_id": data.get("rma_out_type_id", False)}
|
||||
)
|
||||
return data
|
||||
|
||||
def _get_routes_values(self):
|
||||
res = super()._get_routes_values()
|
||||
rma_routes = {
|
||||
"rma_in_route_id": {
|
||||
"routing_key": "rma_in",
|
||||
"depends": ["active"],
|
||||
"route_update_values": {
|
||||
"name": self._format_routename("RMA In"),
|
||||
"active": self.active,
|
||||
},
|
||||
"route_create_values": {
|
||||
"warehouse_selectable": True,
|
||||
"company_id": self.company_id.id,
|
||||
"sequence": 100,
|
||||
},
|
||||
"rules_values": {
|
||||
"active": True,
|
||||
},
|
||||
},
|
||||
"rma_out_route_id": {
|
||||
"routing_key": "rma_out",
|
||||
"depends": ["active"],
|
||||
"route_update_values": {
|
||||
"name": self._format_routename("RMA Out"),
|
||||
"active": self.active,
|
||||
},
|
||||
"route_create_values": {
|
||||
"warehouse_selectable": True,
|
||||
"company_id": self.company_id.id,
|
||||
"sequence": 110,
|
||||
},
|
||||
"rules_values": {
|
||||
"active": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
if self.env.context.get("rma_post_init_hook"):
|
||||
return rma_routes
|
||||
res.update(rma_routes)
|
||||
return res
|
||||
|
||||
def get_rules_dict(self):
|
||||
res = super().get_rules_dict()
|
||||
customer_loc, supplier_loc = self._get_partner_locations()
|
||||
for warehouse in self:
|
||||
res[warehouse.id].update(
|
||||
{
|
||||
"rma_in": [
|
||||
self.Routing(
|
||||
customer_loc,
|
||||
warehouse.rma_loc_id,
|
||||
warehouse.rma_in_type_id,
|
||||
"pull",
|
||||
)
|
||||
],
|
||||
"rma_out": [
|
||||
self.Routing(
|
||||
warehouse.rma_loc_id,
|
||||
customer_loc,
|
||||
warehouse.rma_out_type_id,
|
||||
"pull",
|
||||
)
|
||||
],
|
||||
}
|
||||
)
|
||||
return res
|
||||
Loading…
Add table
Add a link
Reference in a new issue