Initial commit: OCA Technical packages (595 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:03 +02:00
commit 2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import rma_delivery
from . import rma_finalization_wizard
from . import rma_split
from . import stock_picking_return

View file

@ -0,0 +1,95 @@
# Copyright 2020 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class RmaReDeliveryWizard(models.TransientModel):
_name = "rma.delivery.wizard"
_description = "RMA Delivery Wizard"
rma_count = fields.Integer()
type = fields.Selection(
selection=[("replace", "Replace"), ("return", "Return to customer")],
required=True,
)
product_id = fields.Many2one(
comodel_name="product.product",
string="Replace Product",
)
product_uom_qty = fields.Float(
string="Product qty",
digits="Product Unit of Measure",
)
product_uom = fields.Many2one(comodel_name="uom.uom", string="Unit of measure")
scheduled_date = fields.Datetime(required=True, default=fields.Datetime.now)
warehouse_id = fields.Many2one(
comodel_name="stock.warehouse",
string="Warehouse",
required=True,
)
uom_category_id = fields.Many2one(related="product_id.uom_id.category_id")
rma_return_grouping = fields.Boolean(
string="Group RMA returns by customer address and warehouse",
default=lambda self: self.env.company.rma_return_grouping,
)
@api.constrains("product_uom_qty")
def _check_product_uom_qty(self):
self.ensure_one()
rma_ids = self.env.context.get("active_ids")
if len(rma_ids) == 1 and self.product_uom_qty <= 0:
raise ValidationError(_("Quantity must be greater than 0."))
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
rma_ids = self.env.context.get("active_ids")
rma = self.env["rma"].browse(rma_ids)
warehouse_id = (
self.env["stock.warehouse"]
.search([("company_id", "=", rma[0].company_id.id)], limit=1)
.id
)
delivery_type = self.env.context.get("rma_delivery_type")
product_id = False
if len(rma) == 1 and delivery_type == "return":
product_id = rma.product_id.id
product_uom_qty = 0.0
if len(rma) == 1 and rma.remaining_qty > 0.0:
product_uom_qty = rma.remaining_qty
res.update(
rma_count=len(rma),
warehouse_id=warehouse_id,
type=delivery_type,
product_id=product_id,
product_uom_qty=product_uom_qty,
)
return res
@api.onchange("product_id")
def _onchange_product_id(self):
if self.product_id:
if not self.product_uom or self.product_id.uom_id.id != self.product_uom.id:
self.product_uom = self.product_id.uom_id
def action_deliver(self):
self.ensure_one()
rma_ids = self.env.context.get("active_ids")
rma = self.env["rma"].browse(rma_ids)
if self.type == "replace":
rma.create_replace(
self.scheduled_date,
self.warehouse_id,
self.product_id,
self.product_uom_qty,
self.product_uom,
)
elif self.type == "return":
qty = uom = None
if self.rma_count == 1:
qty, uom = self.product_uom_qty, self.product_uom
rma.with_context(
rma_return_grouping=self.rma_return_grouping
).create_return(self.scheduled_date, qty, uom)

View file

@ -0,0 +1,75 @@
<?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">
<field name="name">rma.delivery.wizard.form</field>
<field name="model">rma.delivery.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="scheduled_date" />
<field
name="warehouse_id"
attrs="{'invisible': [('type', '!=', 'replace')]}"
/>
<field
name="rma_return_grouping"
attrs="{'invisible': ['|', ('type', '=', 'replace'), ('rma_count', '=', 1)]}"
/>
</group>
<group>
<field name="uom_category_id" invisible="1" />
<field
name="product_id"
attrs="{'invisible': ['|', ('type', '!=', 'replace'), ('rma_count', '>', 1)], 'required': [('type', '=', 'replace'), ('rma_count', '=', 1)]}"
/>
<label
for="product_uom_qty"
attrs="{'invisible': [('rma_count', '>', 1)]}"
/>
<div
class="o_row"
attrs="{'invisible': [('rma_count', '>', 1)]}"
>
<field
name="product_uom_qty"
attrs="{'required': [('rma_count', '=', 1)]}"
/>
<field
name="product_uom"
groups="uom.group_uom"
attrs="{'required': [('rma_count', '=', 1)]}"
domain="[('category_id', '=', uom_category_id)]"
/>
<field name="product_uom" invisible="1" />
</div>
</group>
</group>
<field name="rma_count" invisible="1" />
<field name="type" invisible="1" />
<footer>
<button
name="action_deliver"
string="Deliver"
type="object"
class="btn-primary"
/>
<button string="Cancel" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="rma_delivery_wizard_action" model="ir.actions.act_window">
<field name="name">Return to customer</field>
<field name="res_model">rma.delivery.wizard</field>
<field name="view_mode">form</field>
<field name="binding_model_id" ref="rma.model_rma" />
<field name="binding_view_types">list</field>
<field name="target">new</field>
<field name="context">{'rma_delivery_type': 'return'}</field>
</record>
</odoo>

View file

@ -0,0 +1,18 @@
# Copyright 2022 Tecnativa - David Vidal
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class RmaFinalizationWizard(models.TransientModel):
_name = "rma.finalization.wizard"
_description = "RMA Finalization Wizard"
finalization_id = fields.Many2one(
comodel_name="rma.finalization", string="Reason", required=True
)
def action_finish(self):
self.ensure_one()
rma_ids = self.env.context.get("active_ids")
rma = self.env["rma"].browse(rma_ids)
rma.write({"finalization_id": self.finalization_id, "state": "finished"})

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2022 Tecnativa - David Vidal
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="rma_finalization_wizard_view_form" model="ir.ui.view">
<field name="model">rma.finalization.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="finalization_id" />
</group>
</group>
<footer>
<button
name="action_finish"
string="Finish RMA"
type="object"
class="btn-primary"
/>
<button string="Cancel" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="rma_finalization_wizard_action" model="ir.actions.act_window">
<field name="name">Finish RMA Manualy</field>
<field name="res_model">rma.finalization.wizard</field>
<field name="view_mode">form</field>
<field name="binding_model_id" ref="rma.model_rma" />
<field name="binding_view_types">list</field>
<field name="target">new</field>
</record>
</odoo>

View file

@ -0,0 +1,70 @@
# Copyright 2020 Tecnativa - Ernesto Tejeda
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class RmaReSplitWizard(models.TransientModel):
_name = "rma.split.wizard"
_description = "RMA Split Wizard"
rma_id = fields.Many2one(
comodel_name="rma",
string="RMA",
)
product_uom_qty = fields.Float(
string="Quantity to extract",
digits="Product Unit of Measure",
required=True,
help="Quantity to extract to a new RMA.",
)
product_uom = fields.Many2one(
comodel_name="uom.uom",
string="Unit of measure",
required=True,
)
_sql_constraints = [
(
"check_product_uom_qty_positive",
"CHECK(product_uom_qty > 0)",
"Quantity must be greater than 0.",
),
]
@api.model
def fields_get(self, allfields=None, attributes=None):
res = super().fields_get(allfields, attributes=attributes)
rma_id = self.env.context.get("active_id")
rma = self.env["rma"].browse(rma_id)
res["product_uom"]["domain"] = [
("category_id", "=", rma.product_uom.category_id.id)
]
return res
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
rma_id = self.env.context.get("active_id")
rma = self.env["rma"].browse(rma_id)
res.update(
rma_id=rma.id,
product_uom_qty=rma.remaining_qty,
product_uom=rma.product_uom.id,
)
return res
def action_split(self):
self.ensure_one()
extracted_rma = self.rma_id.extract_quantity(
self.product_uom_qty, self.product_uom
)
return {
"name": _("Extracted RMA"),
"type": "ir.actions.act_window",
"view_type": "form",
"view_mode": "form",
"res_model": "rma",
"views": [(self.env.ref("rma.rma_view_form").id, "form")],
"res_id": extracted_rma.id,
}

View file

@ -0,0 +1,39 @@
<?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">
<field name="name">rma.split.wizard.form</field>
<field name="model">rma.split.wizard</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<label for="product_uom_qty" />
<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>
<footer>
<button
name="action_split"
string="Split"
type="object"
class="btn-primary"
/>
<button string="Cancel" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="rma_split_wizard_action" model="ir.actions.act_window">
<field name="name">Split RMA</field>
<field name="res_model">rma.split.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>

View file

@ -0,0 +1,179 @@
# 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 copy import deepcopy
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import float_compare
class ReturnPickingLine(models.TransientModel):
_inherit = "stock.return.picking.line"
rma_operation_id = fields.Many2one(
comodel_name="rma.operation",
string="Operation",
compute="_compute_rma_operation_id",
store=True,
readonly=False,
)
return_product_id = fields.Many2one(
"product.product",
help="Product to be returned if it's different from the originally delivered "
"item.",
)
different_return_product = fields.Boolean(
related="rma_operation_id.different_return_product"
)
@api.depends("wizard_id.rma_operation_id")
def _compute_rma_operation_id(self):
for rec in self:
if rec.wizard_id.rma_operation_id:
rec.rma_operation_id = rec.wizard_id.rma_operation_id
def _prepare_rma_vals(self):
self.ensure_one()
return {
"move_id": self.move_id.id,
"product_id": self.move_id.product_id.id,
"product_uom_qty": self.quantity,
"product_uom": self.product_id.uom_id.id,
"location_id": self.wizard_id.location_id.id or self.move_id.location_id.id,
"operation_id": self.rma_operation_id.id,
"return_product_id": self.return_product_id.id,
}
class ReturnPicking(models.TransientModel):
_inherit = "stock.return.picking"
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"
)
rma_operation_id = fields.Many2one(
comodel_name="rma.operation",
string="Requested operation",
)
# Expand domain for RMAs
location_id = fields.Many2one(
domain="create_rma and [('id', 'child_of', rma_location_ids)]"
"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
# 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 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
def _prepare_rma_partner_values(self):
self.ensure_one()
partner = self.picking_id.partner_id
partner_address = partner.address_get(["invoice", "delivery"])
partner_invoice_id = partner_address.get("invoice", False)
partner_shipping_id = partner_address.get("delivery", False)
return (
partner,
partner_invoice_id and partner.browse(partner_invoice_id) or partner,
partner_shipping_id and partner.browse(partner_shipping_id) or partner,
)
def _prepare_rma_vals(self):
partner, partner_invoice, partner_shipping = self._prepare_rma_partner_values()
origin = self.picking_id.name
return {
"user_id": self.env.user.id,
"partner_id": partner.id,
"partner_shipping_id": partner_shipping.id,
"partner_invoice_id": partner_invoice.id,
"origin": origin,
"picking_id": self.picking_id.id,
"company_id": self.company_id.id,
}
def _prepare_rma_vals_list(self):
vals_list = []
for return_picking in self:
global_vals = return_picking._prepare_rma_vals()
for line in return_picking.product_return_moves:
if (
not line.move_id
or float_compare(line.quantity, 0, line.product_id.uom_id.rounding)
<= 0
):
continue
vals = deepcopy(global_vals)
vals.update(line._prepare_rma_vals())
vals_list.append(vals)
return vals_list
def create_returns(self):
"""Override create_returns method for creating one or more
'confirmed' RMAs after return a delivery picking in case
'Create RMAs' checkbox is checked in this wizard.
New RMAs will be linked to the delivery picking as the origin
delivery and also RMAs will be linked to the returned picking
as the 'Receipt'.
"""
if self.create_rma:
if not self.picking_id.partner_id:
raise ValidationError(
_(
"You must specify the 'Customer' in the "
"'Stock Picking' from which RMAs will be created"
)
)
vals_list = self._prepare_rma_vals_list()
rmas = self.env["rma"].create(vals_list)
rmas.action_confirm()
picking = rmas.reception_move_id.picking_id
picking = picking and picking[0] or picking
ctx = dict(self.env.context)
ctx.update(
{
"default_partner_id": picking.partner_id.id,
"search_default_picking_type_id": picking.picking_type_id.id,
"search_default_draft": False,
"search_default_assigned": False,
"search_default_confirmed": False,
"search_default_ready": False,
"search_default_planning_issues": False,
"search_default_available": False,
}
)
return {
"name": _("Returned Picking"),
"view_mode": "form,tree,calendar",
"res_model": "stock.picking",
"res_id": picking.id,
"type": "ir.actions.act_window",
"context": ctx,
}
return super().create_returns()

View file

@ -0,0 +1,40 @@
<?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">
<field name="name">Return lines inherit RMA</field>
<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="//field[@name='product_return_moves']//tree" position="inside">
<field
name="rma_operation_id"
attrs="{'column_invisible': [('parent.create_rma', '=', False)], 'required': [('parent.create_rma', '=', True), ('quantity', '>', 0)]}"
/>
<field
name="return_product_id"
attrs="{'column_invisible': [('parent.create_rma', '=', False)], 'invisible': [('different_return_product', '=', False)], 'required': [('different_return_product', '=', True), ('quantity', '>', 0)]}"
/>
<field name="different_return_product" invisible="1" />
</xpath>
<field name="product_return_moves" position="before">
<group name="group_rma">
<field
name="create_rma"
attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"
/>
<field
name="rma_operation_id"
attrs="{'invisible': [('create_rma', '=', False)]}"
/>
<field name="rma_location_ids" invisible="1" />
<field name="picking_id" invisible="1" />
<field name="picking_type_code" invisible="1" />
<field name="location_id" invisible="1" />
</group>
</field>
</field>
</record>
</odoo>