Merge pull request '[IMP] stock_picking_batch_collect: cantidad demandada en las líneas de movimiento' (#22) from 18.0-imp-stock_picking_batch_collect-demand into 18.0
Some checks failed
CI / lint (push) Successful in 32s
CI / test (push) Failing after 11m2s
CI / review (push) Has been skipped

Reviewed-on: #22
This commit is contained in:
snt 2026-10-10 16:55:16 +00:00
commit 8a029464eb
11 changed files with 93 additions and 1 deletions

View file

@ -761,6 +761,8 @@ last_purchase_price_compute_type != "manual_update" # Para auto-cálculo
`product_pricelist_total_margin`, `product_origin_char`, `l10n_es_edi_tbai_reagyp_recibidas`. `product_pricelist_total_margin`, `product_origin_char`, `l10n_es_edi_tbai_reagyp_recibidas`.
`product_origin` (OCA) sustituido por `product_origin_char` (custom). `product_origin` (OCA) sustituido por `product_origin_char` (custom).
- **2026-06-04**: `product_sale_price_from_pricelist` aplica descuentos de proveedor desde `supplierinfo`. - **2026-06-04**: `product_sale_price_from_pricelist` aplica descuentos de proveedor desde `supplierinfo`.
- **2026-10-10**: `stock_picking_batch_collect` (v18.0.2.2.0) — columna de cantidad demandada en
las listas de líneas de movimiento (operaciones detalladas de lote y albarán, vista operario).
- **2026-10-04**: `stock_picking_batch_collect` (v18.0.2.1.0) — checks de recogido en albaranes - **2026-10-04**: `stock_picking_batch_collect` (v18.0.2.1.0) — checks de recogido en albaranes
sueltos (activables por tipo de operación), columna de cantidad pedida en la venta (depende de sueltos (activables por tipo de operación), columna de cantidad pedida en la venta (depende de
`sale_stock`) e informe *Albaranes* del lote. `sale_stock`) e informe *Albaranes* del lote.

View file

@ -29,6 +29,11 @@ producto y una vista a pantalla completa para el almacén.
comparar lo pedido con lo preparado en la misma pantalla. Está en las comparar lo pedido con lo preparado en la misma pantalla. Está en las
operaciones detalladas, la vista de operario, la pestaña *Operations* y el operaciones detalladas, la vista de operario, la pestaña *Operations* y el
resumen por producto del lote, y en el albarán. resumen por producto del lote, y en el albarán.
- Columna **Cantidad demandada** (``move_demand_qty``), de sólo lectura, en las
listas de líneas de movimiento, que no la traen de serie: las operaciones
detalladas del lote y del albarán y la vista de operario. Es la demanda del
movimiento completo, así que se repite en cada línea de un movimiento
repartido en varias (lotes, ubicaciones).
- Informe **Albaranes** en el lote: todos sus albaranes de entrega en un PDF. - Informe **Albaranes** en el lote: todos sus albaranes de entrega en un PDF.
Las columnas se añaden con ``optional="show"`` en la vista de líneas del lote Las columnas se añaden con ``optional="show"`` en la vista de líneas del lote
@ -136,6 +141,8 @@ Uso
9. La columna **Cantidad pedida** muestra lo que pidió el cliente, frente a la 9. La columna **Cantidad pedida** muestra lo que pidió el cliente, frente a la
demanda (ya ajustada al peso) y la cantidad preparada. En el resumen por demanda (ya ajustada al peso) y la cantidad preparada. En el resumen por
producto del lote es la suma de los pedidos. producto del lote es la suma de los pedidos.
La columna **Cantidad demandada** está junto a ella en todas las vistas,
también en la vista de operario.
10. **Imprimir > Albaranes** en el lote genera un único PDF con el albarán de 10. **Imprimir > Albaranes** en el lote genera un único PDF con el albarán de
entrega de cada uno de sus traslados (salvo los cancelados), ordenados por entrega de cada uno de sus traslados (salvo los cancelados), ordenados por

View file

@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ # noqa: B018 { # noqa: B018
"name": "Stock Picking Batch Collect", "name": "Stock Picking Batch Collect",
"version": "18.0.2.1.0", "version": "18.0.2.2.0",
"category": "Warehouse", "category": "Warehouse",
"summary": "Collect batch operations: operator view, extra columns and product summary", "summary": "Collect batch operations: operator view, extra columns and product summary",
"author": "Odoo Community Association (OCA), Criptomart", "author": "Odoo Community Association (OCA), Criptomart",

View file

@ -105,6 +105,7 @@ msgid "Created on"
msgstr "Creado el" msgstr "Creado el"
#. module: stock_picking_batch_collect #. module: stock_picking_batch_collect
#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move_line__move_demand_qty
#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__qty_demanded #: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__qty_demanded
msgid "Demanded Quantity" msgid "Demanded Quantity"
msgstr "Cantidad demandada" msgstr "Cantidad demandada"
@ -282,3 +283,8 @@ msgid ""
msgstr "" msgstr ""
"Hay que marcar como recogidas las operaciones de %(picking)s antes de validarlo.\n" "Hay que marcar como recogidas las operaciones de %(picking)s antes de validarlo.\n"
"Productos pendientes: %(products)s" "Productos pendientes: %(products)s"
#. module: stock_picking_batch_collect
#: model_terms:ir.ui.view,arch_db:stock_picking_batch_collect.view_move_line_batch_operator
msgid "Demand"
msgstr "Demanda"

View file

@ -104,6 +104,7 @@ msgid "Created on"
msgstr "Noiz sortua" msgstr "Noiz sortua"
#. module: stock_picking_batch_collect #. module: stock_picking_batch_collect
#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move_line__move_demand_qty
#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__qty_demanded #: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__qty_demanded
msgid "Demanded Quantity" msgid "Demanded Quantity"
msgstr "Eskatutako kantitatea" msgstr "Eskatutako kantitatea"
@ -282,3 +283,8 @@ msgid ""
msgstr "" msgstr ""
"%(picking)s balidatu aurretik, bere eragiketak jasota gisa markatu behar dira.\n" "%(picking)s balidatu aurretik, bere eragiketak jasota gisa markatu behar dira.\n"
"Zain dauden produktuak: %(products)s" "Zain dauden produktuak: %(products)s"
#. module: stock_picking_batch_collect
#: model_terms:ir.ui.view,arch_db:stock_picking_batch_collect.view_move_line_batch_operator
msgid "Demand"
msgstr "Eskaria"

View file

@ -41,6 +41,11 @@ class StockMoveLine(models.Model):
digits="Product Unit of Measure", digits="Product Unit of Measure",
compute="_compute_sale_ordered_qty", compute="_compute_sale_ordered_qty",
) )
move_demand_qty = fields.Float(
string="Demanded Quantity",
digits="Product Unit of Measure",
compute="_compute_move_demand_qty",
)
@api.depends("move_id.sale_ordered_qty", "move_id.product_uom", "product_uom_id") @api.depends("move_id.sale_ordered_qty", "move_id.product_uom", "product_uom_id")
def _compute_sale_ordered_qty(self): def _compute_sale_ordered_qty(self):
@ -53,6 +58,18 @@ class StockMoveLine(models.Model):
else: else:
line.sale_ordered_qty = 0.0 line.sale_ordered_qty = 0.0
@api.depends("move_id.product_uom_qty", "move_id.product_uom", "product_uom_id")
def _compute_move_demand_qty(self):
"""The demand of the whole move: every line of a split move shows it."""
for line in self:
move = line.move_id
if move.product_uom and line.product_uom_id:
line.move_demand_qty = move.product_uom._compute_quantity(
move.product_uom_qty, line.product_uom_id, round=False
)
else:
line.move_demand_qty = 0.0
def write(self, vals): def write(self, vals):
res = super().write(vals) res = super().write(vals)
if vals.get("is_collected"): if vals.get("is_collected"):

View file

@ -23,6 +23,11 @@ producto y una vista a pantalla completa para el almacén.
comparar lo pedido con lo preparado en la misma pantalla. Está en las comparar lo pedido con lo preparado en la misma pantalla. Está en las
operaciones detalladas, la vista de operario, la pestaña *Operations* y el operaciones detalladas, la vista de operario, la pestaña *Operations* y el
resumen por producto del lote, y en el albarán. resumen por producto del lote, y en el albarán.
- Columna **Cantidad demandada** (``move_demand_qty``), de sólo lectura, en las
listas de líneas de movimiento, que no la traen de serie: las operaciones
detalladas del lote y del albarán y la vista de operario. Es la demanda del
movimiento completo, así que se repite en cada línea de un movimiento
repartido en varias (lotes, ubicaciones).
- Informe **Albaranes** en el lote: todos sus albaranes de entrega en un PDF. - Informe **Albaranes** en el lote: todos sus albaranes de entrega en un PDF.
Las columnas se añaden con ``optional="show"`` en la vista de líneas del lote Las columnas se añaden con ``optional="show"`` en la vista de líneas del lote

View file

@ -36,6 +36,8 @@ Uso
9. La columna **Cantidad pedida** muestra lo que pidió el cliente, frente a la 9. La columna **Cantidad pedida** muestra lo que pidió el cliente, frente a la
demanda (ya ajustada al peso) y la cantidad preparada. En el resumen por demanda (ya ajustada al peso) y la cantidad preparada. En el resumen por
producto del lote es la suma de los pedidos. producto del lote es la suma de los pedidos.
La columna **Cantidad demandada** está junto a ella en todas las vistas,
también en la vista de operario.
10. **Imprimir > Albaranes** en el lote genera un único PDF con el albarán de 10. **Imprimir > Albaranes** en el lote genera un único PDF con el albarán de
entrega de cada uno de sus traslados (salvo los cancelados), ordenados por entrega de cada uno de sus traslados (salvo los cancelados), ordenados por

View file

@ -110,6 +110,50 @@ class TestPickingCollected(TransactionCase):
self.assertEqual(move.sale_ordered_qty, 2.0) self.assertEqual(move.sale_ordered_qty, 2.0)
self.assertEqual(move.move_line_ids.sale_ordered_qty, 2.0) self.assertEqual(move.move_line_ids.sale_ordered_qty, 2.0)
def test_move_line_demand_follows_the_move(self):
picking = self._create_sale_picking(2.0)
move = picking.move_ids
line = move.move_line_ids
self.assertEqual(line.move_demand_qty, 2.0)
gram = self.env.ref("uom.product_uom_gram")
line.product_uom_id = gram
self.assertEqual(line.move_demand_qty, 2000.0)
move.product_uom_qty = 3.0
self.assertEqual(line.move_demand_qty, 3000.0)
def test_move_line_demand_without_sale(self):
"""Unlike the ordered quantity, the demand needs no sale order."""
picking = self.env["stock.picking"].create(
{
"picking_type_id": self.picking_type_out.id,
"location_id": self.stock_location.id,
"location_dest_id": self.env.ref("stock.stock_location_customers").id,
"move_ids": [
Command.create(
{
"name": self.product.name,
"product_id": self.product.id,
"product_uom_qty": 4.0,
"product_uom": self.product.uom_id.id,
"location_id": self.stock_location.id,
"location_dest_id": self.env.ref(
"stock.stock_location_customers"
).id,
}
)
],
}
)
picking.action_confirm()
picking.action_assign()
line = picking.move_ids.move_line_ids
self.assertEqual(line.move_demand_qty, 4.0)
self.assertEqual(line.sale_ordered_qty, 0.0)
def test_batch_summary_ordered_quantity(self): def test_batch_summary_ordered_quantity(self):
pickings = self._create_sale_picking(2.0) | self._create_sale_picking(3.0) pickings = self._create_sale_picking(2.0) | self._create_sale_picking(3.0)
batch = self.env["stock.picking.batch"].create( batch = self.env["stock.picking.batch"].create(

View file

@ -32,6 +32,7 @@
<field name="reference" readonly="1" optional="hide"/> <field name="reference" readonly="1" optional="hide"/>
<field name="picking_partner_id" readonly="1" optional="show"/> <field name="picking_partner_id" readonly="1" optional="show"/>
<field name="sale_ordered_qty" string="Ordered" readonly="1" optional="show"/> <field name="sale_ordered_qty" string="Ordered" readonly="1" optional="show"/>
<field name="move_demand_qty" string="Demand" readonly="1" optional="show"/>
<field name="quantity" string="Cant."/> <field name="quantity" string="Cant."/>
<!-- Loaded so `_onchange_quantity_manual` reaches the server. --> <!-- Loaded so `_onchange_quantity_manual` reaches the server. -->
<field name="picked" column_invisible="True"/> <field name="picked" column_invisible="True"/>
@ -82,6 +83,7 @@
</xpath> </xpath>
<xpath expr="//field[@name='quantity']" position="before"> <xpath expr="//field[@name='quantity']" position="before">
<field name="sale_ordered_qty" readonly="1" optional="show"/> <field name="sale_ordered_qty" readonly="1" optional="show"/>
<field name="move_demand_qty" readonly="1" optional="show"/>
</xpath> </xpath>
<xpath expr="//field[@name='quantity']" position="after"> <xpath expr="//field[@name='quantity']" position="after">
<!-- Loaded so `_onchange_quantity_manual` reaches the server. --> <!-- Loaded so `_onchange_quantity_manual` reaches the server. -->

View file

@ -25,6 +25,7 @@
<xpath expr="//field[@name='quantity']" position="before"> <xpath expr="//field[@name='quantity']" position="before">
<field name="sale_ordered_qty" readonly="1" optional="show" <field name="sale_ordered_qty" readonly="1" optional="show"
column_invisible="context.get('picking_code') == 'incoming'"/> column_invisible="context.get('picking_code') == 'incoming'"/>
<field name="move_demand_qty" readonly="1" optional="show"/>
</xpath> </xpath>
<xpath expr="//field[@name='quantity']" position="after"> <xpath expr="//field[@name='quantity']" position="after">
<field name="is_collected" widget="boolean_toggle" optional="show" <field name="is_collected" widget="boolean_toggle" optional="show"