Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion stock_ux/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
##############################################################################
{
"name": "Stock UX",
"version": "19.0.1.1.0",
"version": "19.0.2.0.0",
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Se está agregando el campo almacenado is_exchange_move al modelo stock.move (cambio estructural). Debes agregar un script de migración en migrations/19.0.2.0.0/ para asegurar que el campo se inicialice correctamente en registros existentes. Aunque el campo es booleano y PostgreSQL lo inicializará a False por defecto, es recomendable documentar esto explícitamente mediante un script post-migración que registre la operación en logs para futuras referencias.

Copilot generated this review using guidance from repository custom instructions.
"category": "Warehouse Management",
"sequence": 14,
"summary": "",
Expand All @@ -45,6 +45,7 @@
"views/report_deliveryslip.xml",
"views/res_config_settings_views.xml",
"wizards/stock_operation_wizard_views.xml",
"wizards/stock_return_picking_views.xml",
"report/ir.action.reports.xml",
"report/picking_templates.xml",
"views/res_company_views.xml",
Expand Down
9 changes: 7 additions & 2 deletions stock_ux/models/stock_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,19 @@ class StockMove(models.Model):
lots_visible = fields.Boolean(
related="move_line_ids.lots_visible",
)

picking_partner_id = fields.Many2one(
"res.partner",
"Transfer Destination Address",
related="picking_id.partner_id",
)

origin_description = fields.Char(compute="_compute_origin_description", compute_sudo=True)
is_exchange_move = fields.Boolean()

@api.model
def _prepare_merge_moves_distinct_fields(self):
fields = super()._prepare_merge_moves_distinct_fields()
fields.append("is_exchange_move")
return fields

@api.depends(
"move_line_ids.quantity",
Expand Down
10 changes: 10 additions & 0 deletions stock_ux/models/stock_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,13 @@ def _compute_propagate_carrier(self):
"""Make True by default if picking code is outgoing"""
for rec in self:
rec.propagate_carrier = rec.picking_type_id.code == "outgoing"

def _get_stock_move_values(
self, product_id, product_qty, product_uom, location_dest_id, name, origin, company_id, values
):
move_values = super()._get_stock_move_values(
product_id, product_qty, product_uom, location_dest_id, name, origin, company_id, values
)
if self.env.context.get("is_exchange_move"):
move_values["is_exchange_move"] = True
return move_values
1 change: 1 addition & 0 deletions stock_ux/wizards/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
##############################################################################
from . import stock_operation_wizard
from . import stock_label_type
from . import stock_return_picking
45 changes: 45 additions & 0 deletions stock_ux/wizards/stock_return_picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
##############################################################################
# For copyright and license notices, see __manifest__.py file in module root
# directory
##############################################################################
from odoo import _, api, models
from odoo.exceptions import UserError


class StockReturnPicking(models.TransientModel):
_inherit = "stock.return.picking"

@api.model
def default_get(self, fields):
"""Get sale order for lines."""
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El docstring "Get sale order for lines." no describe correctamente lo que hace el método. Este método está configurando to_refund=True por defecto en las líneas de devolución y no tiene ninguna lógica específica de órdenes de venta. Debes actualizar el docstring para reflejar el comportamiento real, por ejemplo: "Set to_refund to True by default for all product return move lines."

Suggested change
"""Get sale order for lines."""
"""Set to_refund to True by default for all product return move lines."""

Copilot uses AI. Check for mistakes.
result = super().default_get(fields)
try:
for line in result["product_return_moves"]:
assert line[0] == 0
# en realidad no nos importa si hay linea de venta o no ya que
# también lo usamos en compras y queremos que en todo caso este
line[2]["to_refund"] = True
except KeyError:
pass
Comment on lines +16 to +23
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El uso de assert en código de producción no es apropiado. Las assertions se eliminan cuando Python se ejecuta en modo optimizado (-O). Si necesitas validar que line[0] == 0, debes usar una validación explícita con una excepción adecuada, o mejor aún, simplemente procesar las líneas sin asumir su estructura. Considera reemplazarlo por una validación condicional o removerlo si no es estrictamente necesario.

Suggested change
try:
for line in result["product_return_moves"]:
assert line[0] == 0
# en realidad no nos importa si hay linea de venta o no ya que
# también lo usamos en compras y queremos que en todo caso este
line[2]["to_refund"] = True
except KeyError:
pass
for line in result.get("product_return_moves", []):
if (
isinstance(line, (list, tuple))
and len(line) > 2
and line[0] == 0
and isinstance(line[2], dict)
):
# en realidad no nos importa si hay linea de venta o no ya que
# también lo usamos en compras y queremos que en todo caso este
line[2]["to_refund"] = True

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +23
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
try:
for line in result["product_return_moves"]:
assert line[0] == 0
# en realidad no nos importa si hay linea de venta o no ya que
# también lo usamos en compras y queremos que en todo caso este
line[2]["to_refund"] = True
except KeyError:
pass
if "product_return_moves" in result:
for line in result["product_return_moves"]:
assert line[0] == 0
# en realidad no nos importa si hay linea de venta o no ya que
# también lo usamos en compras y queremos que en todo caso este
line[2]["to_refund"] = True

Copilot uses AI. Check for mistakes.
return result

def action_create_exchanges(self):
if any(self.product_return_moves.mapped("to_refund")):
raise UserError(_("You cannot create exchanges for return lines marked to refund."))
return super(StockReturnPicking, self.with_context(is_exchange_move=True)).action_create_exchanges()


class StockReturnPickingLine(models.TransientModel):
_inherit = "stock.return.picking.line"

def _prepare_move_default_values(self, new_picking):
vals = super()._prepare_move_default_values(new_picking)
if self.env.context.get("is_exchange_move"):
vals["is_exchange_move"] = True
return vals

def _prepare_picking_default_values_based_on(self, picking):
vals = super()._prepare_picking_default_values_based_on(picking)
if self.env.context.get("is_exchange_move"):
vals["is_exchange_move"] = True
return vals
Comment on lines +42 to +45
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El método _prepare_picking_default_values_based_on intenta asignar is_exchange_move al diccionario de valores del picking, pero el campo is_exchange_move no existe en el modelo stock.picking. Solo está definido en stock.move (línea 34 de stock_move.py). Esto causará que el valor se ignore silenciosamente o genere un error al intentar crear/escribir el picking. Debes agregar el campo is_exchange_move también al modelo stock.picking si es necesario marcarlo a nivel de transferencia, o remover esta asignación si solo se necesita a nivel de movimiento.

Suggested change
vals = super()._prepare_picking_default_values_based_on(picking)
if self.env.context.get("is_exchange_move"):
vals["is_exchange_move"] = True
return vals
return super()._prepare_picking_default_values_based_on(picking)

Copilot uses AI. Check for mistakes.
33 changes: 33 additions & 0 deletions stock_ux/wizards/stock_return_picking_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_stock_return_picking_form" model="ir.ui.view">
<field name="name">Improve refunds wizard</field>
<field name="inherit_id" ref="stock.view_stock_return_picking_form"/>
<field name="model">stock.return.picking</field>
<field name="arch" type="xml">
<field name="product_return_moves" position="before">
<div class="alert alert-info" role="alert" style="margin-bottom:0px;">
<b>Importante</b>: si está haciendo un movimiento inverso relacionado a un Pedido de Venta o de Compra, entonces debe prestar atención al campo <b>"Para Abonar (actualizar OC/OV)"</b>, donde, si:
<ul>
<li>
<b>Está marcado</b>: entonces <b>se va</b> a actualizar la OC/OV considerando que los productos devueltos no se van a volver a entregar, y por ende, permitiendo hacer una nota de crédito si los items ya fueron facturados, o tener en cuenta esta devolución al hacer la factura.
</li>
<li>
<b>No está marcado</b>: entonces <b>no se va</b> a actualizar la OC/OV. Esto es común, por ejemplo, si se devuelve mercadería defectuosa y se pretende hacer una nueva entrega de la misma.
</li>
</ul>
Si la devolución no está ligada a una OC/OV entonces este campo no repercute en nada.
</div>
</field>
<field name="to_refund" position="attributes">
<!-- porque lo queremos usar tmb en oc y esto lo hace no marcable -->
<attribute name="attrs"></attribute>
<!-- always show the button -->
<attribute name="groups"></attribute>
<!-- porque la traducción no se actualiza -->
<attribute name="string">Para Abonar (actualizar OC/OV)</attribute>
</field>
</field>
</record>

</odoo>
Loading