diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 22d616b..0f33715 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -235,7 +235,7 @@ addons-cm/ **Logística y contabilidad** -- [stock_picking_batch_collect](../stock_picking_batch_collect/README.rst) - Columnas extra, resumen por producto y vista operario para batch picking; checks de recogido también en albaranes sueltos, cantidad pedida en venta e informe con todos los albaranes del lote +- [stock_picking_batch_collect](../stock_picking_batch_collect/README.rst) - Columnas extra, resumen por producto y vista operario para batch picking - [web_list_striped](../web_list_striped/README.rst) - Efecto cebra en todas las listas del backend (sólo CSS) - [account_banking_mandate_batch](../account_banking_mandate_batch/README.rst) - Creación masiva de mandatos SEPA desde contactos - [l10n_es_edi_tbai_reagyp_recibidas](../l10n_es_edi_tbai_reagyp_recibidas/README.rst) - Fix TicketBAI REAGYP en facturas recibidas (clave régimen 19 → 02) @@ -761,9 +761,6 @@ 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_origin` (OCA) sustituido por `product_origin_char` (custom). - **2026-06-04**: `product_sale_price_from_pricelist` aplica descuentos de proveedor desde `supplierinfo`. -- **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 - `sale_stock`) e informe *Albaranes* del lote. - **2026-08-27**: `stock_picking_batch_custom` renombrado a `stock_picking_batch_collect` (v18.0.2.0.0). Se invierte la dependencia: ya no depende de `website_sale_aplicoop`; es este el que depende de él e inyecta las columnas de grupo de consumo. El efecto cebra global se diff --git a/stock_picking_batch_collect/README.rst b/stock_picking_batch_collect/README.rst index ab69239..75a70b6 100644 --- a/stock_picking_batch_collect/README.rst +++ b/stock_picking_batch_collect/README.rst @@ -21,15 +21,6 @@ producto y una vista a pantalla completa para el almacén. cantidades grandes y controles táctiles, pensada para tablet en el almacén. - Restricciones de validación configurables por compañía: el lote no se puede validar hasta que las líneas estén marcadas como recogidas. -- Los mismos checks en los albaranes sueltos (fuera de un lote), activables por - tipo de operación. -- Columna **Cantidad pedida** (``sale_ordered_qty``), de sólo lectura, con lo - que pidió el cliente en el pedido de venta. Al teclear el peso real la demanda - del movimiento se ajusta a lo pesado, así que esta columna es la que permite - 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 - resumen por producto del lote, y en el albarán. -- 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 (salvo ``product_default_code``, oculta por defecto), de modo que el usuario @@ -77,17 +68,6 @@ Cada restricción tiene un alcance: mayor que cero (por defecto). - *Todas*: se comprueban todas las líneas del lote, procesadas o no. -Albaranes fuera de un lote --------------------------- - -Para exigir los checks también en los albaranes sueltos, marcar **Exigir líneas -recogidas** en el tipo de operación (**Inventory > Configuration > Operation -Types**, p. ej. *Delivery Orders*). Viene desactivado: sin él, los albaranes -sueltos de ese tipo se validan como siempre. - -Un albarán suelto no tiene resumen por producto, así que las dos restricciones -comprueban sus líneas; si alguna usa el alcance *Todas*, se aplica ese. - Columnas -------- @@ -128,19 +108,6 @@ Uso La comprobación se ejecuta después de resolver el backorder, de modo que sólo bloquea sobre las líneas que realmente se van a procesar. -8. En un albarán suelto, la pestaña **Operations** tiene un check - **Collected** por producto, que marca todas sus líneas; las operaciones - detalladas lo tienen por línea. Si su tipo de operación lo exige, el - albarán no se valida hasta marcarlos. - -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 - producto del lote es la suma de los pedidos. - -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 - cliente. - Contribuidores ============== diff --git a/stock_picking_batch_collect/__init__.py b/stock_picking_batch_collect/__init__.py index 7efee8a..4a5c3e3 100644 --- a/stock_picking_batch_collect/__init__.py +++ b/stock_picking_batch_collect/__init__.py @@ -1,3 +1,2 @@ from . import models # noqa: F401 -from . import report # noqa: F401 from .hooks import pre_init_hook # noqa: F401 diff --git a/stock_picking_batch_collect/__manifest__.py b/stock_picking_batch_collect/__manifest__.py index ae2b5f3..7e61eac 100644 --- a/stock_picking_batch_collect/__manifest__.py +++ b/stock_picking_batch_collect/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { # noqa: B018 "name": "Stock Picking Batch Collect", - "version": "18.0.2.1.0", + "version": "18.0.2.0.0", "category": "Warehouse", "summary": "Collect batch operations: operator view, extra columns and product summary", "author": "Odoo Community Association (OCA), Criptomart", @@ -10,9 +10,6 @@ "website": "https://github.com/Criptomart", "license": "AGPL-3", "depends": [ - # The ordered quantity of the sale line, which the weighed demand - # overwrites. - "sale_stock", # A quantity typed by the operator is the weighed one: it must not be # re-reserved by the scheduler. "stock_move_manual_quantity", @@ -22,8 +19,6 @@ "security/ir.model.access.csv", "views/res_config_settings_views.xml", "views/stock_picking_batch_views.xml", - "views/stock_picking_views.xml", - "report/report_batch_deliveryslip.xml", ], "assets": { "web.assets_backend": [ diff --git a/stock_picking_batch_collect/i18n/es.po b/stock_picking_batch_collect/i18n/es.po index 21f6002..4bad2cb 100644 --- a/stock_picking_batch_collect/i18n/es.po +++ b/stock_picking_batch_collect/i18n/es.po @@ -62,7 +62,6 @@ msgid "Batch Transfer" msgstr "Traslado por lote" #. module: stock_picking_batch_collect -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move__is_collected #: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move_line__is_collected #: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__is_collected msgid "Collected" @@ -234,51 +233,3 @@ msgstr "Si este albarán incluye entrega a domicilio (del pedido de venta)" #: model_terms:ir.ui.view,arch_db:stock_picking_batch_collect.view_stock_picking_batch_form_inherit_summary msgid "Basket Assembly" msgstr "Montaje de Cestas" - -#. module: stock_picking_batch_collect -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move__sale_ordered_qty -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move_line__sale_ordered_qty -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__qty_ordered -msgid "Ordered Quantity" -msgstr "Cantidad pedida" - -#. module: stock_picking_batch_collect -#: model_terms:ir.ui.view,arch_db:stock_picking_batch_collect.view_move_line_batch_operator -msgid "Ordered" -msgstr "Pedido" - -#. module: stock_picking_batch_collect -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_type__collected_check_required -msgid "Require Collected Lines" -msgstr "Exigir líneas recogidas" - -#. module: stock_picking_batch_collect -#: model:ir.model.fields,help:stock_picking_batch_collect.field_stock_picking_type__collected_check_required -msgid "" -"Transfers of this type that are not in a batch can only be validated once " -"their lines are marked as collected, following the batch validation " -"restrictions of the inventory settings." -msgstr "" -"Los albaranes de este tipo que no estén en un lote solo se pueden validar " -"cuando sus líneas estén marcadas como recogidas, según las restricciones de " -"validación de lotes de los ajustes de inventario." - -#. module: stock_picking_batch_collect -#: model:ir.actions.report,name:stock_picking_batch_collect.action_report_batch_deliveryslip -msgid "Delivery Slips" -msgstr "Albaranes" - -#. module: stock_picking_batch_collect -#: model:ir.model,name:stock_picking_batch_collect.model_report_stock_picking_batch_collect_report_batch_deliveryslip -msgid "Batch Delivery Slips" -msgstr "Albaranes del lote" - -#. module: stock_picking_batch_collect -#: code:addons/stock_picking_batch_collect/models/stock_picking.py:0 -#, python-format -msgid "" -"You must mark the operations of %(picking)s as collected before validating it.\n" -"Pending products: %(products)s" -msgstr "" -"Hay que marcar como recogidas las operaciones de %(picking)s antes de validarlo.\n" -"Productos pendientes: %(products)s" diff --git a/stock_picking_batch_collect/i18n/eu.po b/stock_picking_batch_collect/i18n/eu.po index b0de814..62cd2c9 100644 --- a/stock_picking_batch_collect/i18n/eu.po +++ b/stock_picking_batch_collect/i18n/eu.po @@ -62,7 +62,6 @@ msgid "Batch Transfer" msgstr "Lote-transferentzia" #. module: stock_picking_batch_collect -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move__is_collected #: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move_line__is_collected #: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__is_collected msgid "Collected" @@ -234,51 +233,3 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:stock_picking_batch_collect.view_stock_picking_batch_form_inherit_summary msgid "Basket Assembly" msgstr "Saskien Prestaketa" - -#. module: stock_picking_batch_collect -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move__sale_ordered_qty -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_move_line__sale_ordered_qty -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_batch_summary_line__qty_ordered -msgid "Ordered Quantity" -msgstr "Eskaeraren kantitatea" - -#. module: stock_picking_batch_collect -#: model_terms:ir.ui.view,arch_db:stock_picking_batch_collect.view_move_line_batch_operator -msgid "Ordered" -msgstr "Eskatua" - -#. module: stock_picking_batch_collect -#: model:ir.model.fields,field_description:stock_picking_batch_collect.field_stock_picking_type__collected_check_required -msgid "Require Collected Lines" -msgstr "Jasotako lerroak eskatu" - -#. module: stock_picking_batch_collect -#: model:ir.model.fields,help:stock_picking_batch_collect.field_stock_picking_type__collected_check_required -msgid "" -"Transfers of this type that are not in a batch can only be validated once " -"their lines are marked as collected, following the batch validation " -"restrictions of the inventory settings." -msgstr "" -"Lote batean ez dauden mota honetako transferentziak beren lerroak jasota " -"gisa markatuta daudenean bakarrik balida daitezke, inbentarioaren " -"ezarpenetako loteen balidazio-murrizketen arabera." - -#. module: stock_picking_batch_collect -#: model:ir.actions.report,name:stock_picking_batch_collect.action_report_batch_deliveryslip -msgid "Delivery Slips" -msgstr "Entrega-albaranak" - -#. module: stock_picking_batch_collect -#: model:ir.model,name:stock_picking_batch_collect.model_report_stock_picking_batch_collect_report_batch_deliveryslip -msgid "Batch Delivery Slips" -msgstr "Loteko entrega-albaranak" - -#. module: stock_picking_batch_collect -#: code:addons/stock_picking_batch_collect/models/stock_picking.py:0 -#, python-format -msgid "" -"You must mark the operations of %(picking)s as collected before validating it.\n" -"Pending products: %(products)s" -msgstr "" -"%(picking)s balidatu aurretik, bere eragiketak jasota gisa markatu behar dira.\n" -"Zain dauden produktuak: %(products)s" diff --git a/stock_picking_batch_collect/migrations/18.0.2.1.0/post-migration.py b/stock_picking_batch_collect/migrations/18.0.2.1.0/post-migration.py deleted file mode 100644 index d46ffbc..0000000 --- a/stock_picking_batch_collect/migrations/18.0.2.1.0/post-migration.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2026 Criptomart -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import SUPERUSER_ID -from odoo import api - - -def migrate(cr, version): - """Fill the new ordered quantity of the open batches' product summary.""" - env = api.Environment(cr, SUPERUSER_ID, {}) - batches = env["stock.picking.batch"].search( - [("state", "in", ("draft", "in_progress"))] - ) - env.add_to_compute(env["stock.picking.batch"]._fields["summary_line_ids"], batches) - batches.flush_recordset(["summary_line_ids"]) diff --git a/stock_picking_batch_collect/models/__init__.py b/stock_picking_batch_collect/models/__init__.py index 08ec35b..5b85812 100644 --- a/stock_picking_batch_collect/models/__init__.py +++ b/stock_picking_batch_collect/models/__init__.py @@ -1,8 +1,6 @@ from . import res_company # noqa: F401 from . import res_config_settings # noqa: F401 from . import stock_backorder_confirmation # noqa: F401 -from . import stock_move # noqa: F401 from . import stock_move_line # noqa: F401 from . import stock_picking # noqa: F401 from . import stock_picking_batch # noqa: F401 -from . import stock_picking_type # noqa: F401 diff --git a/stock_picking_batch_collect/models/stock_backorder_confirmation.py b/stock_picking_batch_collect/models/stock_backorder_confirmation.py index 6c2067a..2236e4f 100644 --- a/stock_picking_batch_collect/models/stock_backorder_confirmation.py +++ b/stock_picking_batch_collect/models/stock_backorder_confirmation.py @@ -7,15 +7,17 @@ from odoo import models class StockBackorderConfirmation(models.TransientModel): _inherit = "stock.backorder.confirmation" - def _check_collected_products(self): - self.env["stock.picking"].browse( + def _check_batch_summary_lines(self): + pickings = self.env["stock.picking"].browse( self.env.context.get("button_validate_picking_ids", []) - )._check_collected_products() + ) + for batch in pickings.batch_id: + batch._check_all_products_collected(pickings) def process(self): - self._check_collected_products() + self._check_batch_summary_lines() return super().process() def process_cancel_backorder(self): - self._check_collected_products() + self._check_batch_summary_lines() return super().process_cancel_backorder() diff --git a/stock_picking_batch_collect/models/stock_move.py b/stock_picking_batch_collect/models/stock_move.py deleted file mode 100644 index 14e4554..0000000 --- a/stock_picking_batch_collect/models/stock_move.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2026 Criptomart -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api -from odoo import fields -from odoo import models - - -class StockMove(models.Model): - _inherit = "stock.move" - - is_collected = fields.Boolean( - string="Collected", - compute="_compute_is_collected", - inverse="_inverse_is_collected", - ) - sale_ordered_qty = fields.Float( - string="Ordered Quantity", - digits="Product Unit of Measure", - compute="_compute_sale_ordered_qty", - ) - - @api.depends("move_line_ids.is_collected") - def _compute_is_collected(self): - for move in self: - lines = move.move_line_ids - move.is_collected = bool(lines) and all(lines.mapped("is_collected")) - - def _inverse_is_collected(self): - collected = self.filtered("is_collected") - collected.move_line_ids.filtered( - lambda line: not line.is_collected - ).is_collected = True - (self - collected).move_line_ids.filtered("is_collected").is_collected = False - - @api.depends( - "product_id", - "product_uom", - "sale_line_id.product_id", - "sale_line_id.product_uom", - "sale_line_id.product_uom_qty", - ) - def _compute_sale_ordered_qty(self): - """What the customer ordered, which the weighed demand overwrites. - - Kit components carry the kit's sale line, whose quantity is not theirs. - """ - for move in self: - line = move.sale_line_id - if line and line.product_id == move.product_id and move.product_uom: - move.sale_ordered_qty = line.product_uom._compute_quantity( - line.product_uom_qty, move.product_uom, round=False - ) - else: - move.sale_ordered_qty = 0.0 diff --git a/stock_picking_batch_collect/models/stock_move_line.py b/stock_picking_batch_collect/models/stock_move_line.py index 012813f..c87320a 100644 --- a/stock_picking_batch_collect/models/stock_move_line.py +++ b/stock_picking_batch_collect/models/stock_move_line.py @@ -1,7 +1,6 @@ # Copyright 2026 Criptomart # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api from odoo import fields from odoo import models @@ -36,23 +35,6 @@ class StockMoveLine(models.Model): copy=False, ) - sale_ordered_qty = fields.Float( - string="Ordered Quantity", - digits="Product Unit of Measure", - compute="_compute_sale_ordered_qty", - ) - - @api.depends("move_id.sale_ordered_qty", "move_id.product_uom", "product_uom_id") - def _compute_sale_ordered_qty(self): - for line in self: - move = line.move_id - if move.sale_ordered_qty and line.product_uom_id: - line.sale_ordered_qty = move.product_uom._compute_quantity( - move.sale_ordered_qty, line.product_uom_id, round=False - ) - else: - line.sale_ordered_qty = 0.0 - def write(self, vals): res = super().write(vals) if vals.get("is_collected"): diff --git a/stock_picking_batch_collect/models/stock_picking.py b/stock_picking_batch_collect/models/stock_picking.py index ed1b66a..1cb06ef 100644 --- a/stock_picking_batch_collect/models/stock_picking.py +++ b/stock_picking_batch_collect/models/stock_picking.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import models -from odoo.exceptions import UserError class StockPicking(models.Model): @@ -20,68 +19,21 @@ class StockPicking(models.Model): if res is not True: return res - self._check_collected_products() + self._check_batch_collected_products() return res - def _get_open_move_lines(self, scope): - """Move lines still to process; ``processed`` keeps those with quantity.""" - lines = self.move_line_ids.filtered( - lambda line: line.move_id.state not in ("cancel", "done") - and line.product_id - ) - if scope == "processed": - lines = lines.filtered(lambda line: line.move_id.quantity > 0) - return lines - - def _get_not_collected_move_lines(self, scope): - return self._get_open_move_lines(scope).filtered( - lambda line: not line.is_collected - ) - - def _check_collected_products(self): + def _check_batch_collected_products(self): for batch in self.batch_id: batch_pickings = self.filtered( lambda picking, batch=batch: picking.batch_id == batch ) - if batch_pickings._get_open_move_lines("processed"): - batch._check_all_products_collected(batch_pickings) - - loose_pickings = self.filtered( - lambda picking: not picking.batch_id - and picking.picking_type_id.collected_check_required - ) - for picking in loose_pickings: - if picking._get_open_move_lines("processed"): - picking._check_picking_collected() - - def _check_picking_collected(self): - """Apply the batch restrictions to a transfer outside a batch. - - A single transfer has no product summary: the toggle of its Operations - tab collects every line of the product, so both restrictions check its - move lines, with the widest scope enabled. - """ - self.ensure_one() - company = self.company_id - scopes = set() - if company.batch_detailed_restriction_enabled: - scopes.add(company.batch_detailed_restriction_scope) - if company.batch_summary_restriction_enabled: - scopes.add(company.batch_summary_restriction_scope) - if not scopes: - return - scope = "all" if "all" in scopes else "processed" - not_collected = self._get_not_collected_move_lines(scope) - if not not_collected: - return - product_names = ", ".join( - sorted(set(not_collected.mapped("product_id.display_name"))) - ) - raise UserError( - self.env._( - "You must mark the operations of %(picking)s as collected " - "before validating it.\nPending products: %(products)s", - picking=self.name, - products=product_names, + processed_move_lines = batch_pickings.move_line_ids.filtered( + lambda line: ( + line.move_id.state not in ("cancel", "done") + and line.product_id + and line.move_id.quantity > 0 + ) ) - ) + if not processed_move_lines: + continue + batch._check_all_products_collected(batch_pickings) diff --git a/stock_picking_batch_collect/models/stock_picking_batch.py b/stock_picking_batch_collect/models/stock_picking_batch.py index d362c42..553796a 100644 --- a/stock_picking_batch_collect/models/stock_picking_batch.py +++ b/stock_picking_batch_collect/models/stock_picking_batch.py @@ -25,12 +25,10 @@ class StockPickingBatch(models.Model): "move_line_ids.move_id.quantity", "move_line_ids.move_id.product_uom", "move_line_ids.move_id.state", - "move_line_ids.move_id.sale_line_id.product_uom_qty", ) def _compute_summary_line_ids(self): """Aggregate move quantities per product and keep collected flag. - - Ordered: sale line quantity converted to product UoM, once per line. - Demand: move.product_uom_qty converted to product UoM. - Done: move.quantity converted to product UoM. - Pending: demand - done. @@ -51,7 +49,6 @@ class StockPickingBatch(models.Model): } # Use regular dict to avoid accidental creation of empty entries aggregates = {} - counted_sale_lines = set() # Demand per move (count once per move) for move in batch.move_ids: @@ -66,22 +63,10 @@ class StockPickingBatch(models.Model): aggregates[product.id] = { "product_id": product.id, "product_uom_id": product_uom.id, - "qty_ordered": 0.0, "qty_demanded": 0.0, "qty_done": 0.0, } aggregates[product.id]["qty_demanded"] += demand - if move.sale_ordered_qty and move.sale_line_id.id not in ( - counted_sale_lines - ): - counted_sale_lines.add(move.sale_line_id.id) - aggregates[product.id][ - "qty_ordered" - ] += move.product_uom._compute_quantity( - move.sale_ordered_qty, - product_uom, - rounding_method="HALF-UP", - ) # Done per move line for line in batch.move_line_ids: @@ -99,7 +84,6 @@ class StockPickingBatch(models.Model): aggregates[product.id] = { "product_id": product.id, "product_uom_id": product_uom.id, - "qty_ordered": 0.0, "qty_demanded": 0.0, "qty_done": 0.0, } @@ -124,7 +108,6 @@ class StockPickingBatch(models.Model): existing_line.id, { "product_uom_id": values["product_uom_id"], - "qty_ordered": values["qty_ordered"], "qty_demanded": values["qty_demanded"], "qty_done": values["qty_done"], "is_collected": is_collected, @@ -139,7 +122,6 @@ class StockPickingBatch(models.Model): { "product_id": values["product_id"], "product_uom_id": values["product_uom_id"], - "qty_ordered": values["qty_ordered"], "qty_demanded": values["qty_demanded"], "qty_done": values["qty_done"], "is_collected": is_collected, @@ -169,16 +151,30 @@ class StockPickingBatch(models.Model): def _get_not_collected_summary_lines(self, batch_pickings, scope): summary_lines = self.summary_line_ids if scope == "processed": - processed_products = batch_pickings._get_open_move_lines( - "processed" - ).product_id + processed_product_ids = set( + batch_pickings.move_line_ids.filtered( + lambda line: ( + line.move_id.state not in ("cancel", "done") + and line.product_id + and line.move_id.quantity > 0 + ) + ).mapped("product_id.id") + ) summary_lines = summary_lines.filtered( - lambda line: line.product_id in processed_products + lambda line: line.product_id.id in processed_product_ids ) return summary_lines.filtered(lambda line: not line.is_collected) def _get_not_collected_detailed_lines(self, batch_pickings, scope): - return batch_pickings._get_not_collected_move_lines(scope) + detailed_lines = batch_pickings.move_line_ids.filtered( + lambda line: line.move_id.state not in ("cancel", "done") + and line.product_id + ) + if scope == "processed": + detailed_lines = detailed_lines.filtered( + lambda line: line.move_id.quantity > 0 + ) + return detailed_lines.filtered(lambda line: not line.is_collected) def _check_all_products_collected(self, pickings=None): """Validate collected restrictions based on tab configuration.""" @@ -289,11 +285,6 @@ class StockPickingBatchSummaryLine(models.Model): string="Unit of Measure", required=True, ) - qty_ordered = fields.Float( - string="Ordered Quantity", - digits="Product Unit of Measure", - default=0.0, - ) qty_demanded = fields.Float( string="Demanded Quantity", digits="Product Unit of Measure", diff --git a/stock_picking_batch_collect/models/stock_picking_type.py b/stock_picking_batch_collect/models/stock_picking_type.py deleted file mode 100644 index 9b5ff5b..0000000 --- a/stock_picking_batch_collect/models/stock_picking_type.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2026 Criptomart -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import fields -from odoo import models - - -class StockPickingType(models.Model): - _inherit = "stock.picking.type" - - collected_check_required = fields.Boolean( - string="Require Collected Lines", - help="Transfers of this type that are not in a batch can only be " - "validated once their lines are marked as collected, following the " - "batch validation restrictions of the inventory settings.", - ) diff --git a/stock_picking_batch_collect/readme/CONFIGURE.rst b/stock_picking_batch_collect/readme/CONFIGURE.rst index 100fd1a..bda97fc 100644 --- a/stock_picking_batch_collect/readme/CONFIGURE.rst +++ b/stock_picking_batch_collect/readme/CONFIGURE.rst @@ -21,17 +21,6 @@ Cada restricción tiene un alcance: mayor que cero (por defecto). - *Todas*: se comprueban todas las líneas del lote, procesadas o no. -Albaranes fuera de un lote --------------------------- - -Para exigir los checks también en los albaranes sueltos, marcar **Exigir líneas -recogidas** en el tipo de operación (**Inventory > Configuration > Operation -Types**, p. ej. *Delivery Orders*). Viene desactivado: sin él, los albaranes -sueltos de ese tipo se validan como siempre. - -Un albarán suelto no tiene resumen por producto, así que las dos restricciones -comprueban sus líneas; si alguna usa el alcance *Todas*, se aplica ese. - Columnas -------- diff --git a/stock_picking_batch_collect/readme/DESCRIPTION.rst b/stock_picking_batch_collect/readme/DESCRIPTION.rst index 74012d7..dc348e7 100644 --- a/stock_picking_batch_collect/readme/DESCRIPTION.rst +++ b/stock_picking_batch_collect/readme/DESCRIPTION.rst @@ -15,15 +15,6 @@ producto y una vista a pantalla completa para el almacén. cantidades grandes y controles táctiles, pensada para tablet en el almacén. - Restricciones de validación configurables por compañía: el lote no se puede validar hasta que las líneas estén marcadas como recogidas. -- Los mismos checks en los albaranes sueltos (fuera de un lote), activables por - tipo de operación. -- Columna **Cantidad pedida** (``sale_ordered_qty``), de sólo lectura, con lo - que pidió el cliente en el pedido de venta. Al teclear el peso real la demanda - del movimiento se ajusta a lo pesado, así que esta columna es la que permite - 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 - resumen por producto del lote, y en el albarán. -- 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 (salvo ``product_default_code``, oculta por defecto), de modo que el usuario diff --git a/stock_picking_batch_collect/readme/USAGE.rst b/stock_picking_batch_collect/readme/USAGE.rst index 8caafd5..73eea25 100644 --- a/stock_picking_batch_collect/readme/USAGE.rst +++ b/stock_picking_batch_collect/readme/USAGE.rst @@ -27,16 +27,3 @@ Uso las líneas estén recogidas y se avisa con la lista de productos pendientes. La comprobación se ejecuta después de resolver el backorder, de modo que sólo bloquea sobre las líneas que realmente se van a procesar. - -8. En un albarán suelto, la pestaña **Operations** tiene un check - **Collected** por producto, que marca todas sus líneas; las operaciones - detalladas lo tienen por línea. Si su tipo de operación lo exige, el - albarán no se valida hasta marcarlos. - -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 - producto del lote es la suma de los pedidos. - -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 - cliente. diff --git a/stock_picking_batch_collect/report/__init__.py b/stock_picking_batch_collect/report/__init__.py deleted file mode 100644 index dfbb06d..0000000 --- a/stock_picking_batch_collect/report/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import report_batch_deliveryslip # noqa: F401 diff --git a/stock_picking_batch_collect/report/report_batch_deliveryslip.py b/stock_picking_batch_collect/report/report_batch_deliveryslip.py deleted file mode 100644 index c1e57f4..0000000 --- a/stock_picking_batch_collect/report/report_batch_deliveryslip.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2026 Criptomart -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api -from odoo import models - - -class ReportBatchDeliverySlip(models.AbstractModel): - _name = "report.stock_picking_batch_collect.report_batch_deliveryslip" - _description = "Batch Delivery Slips" - - @api.model - def _get_report_values(self, docids, data=None): - batches = self.env["stock.picking.batch"].browse(docids) - pickings = self.env["stock.picking"] - for batch in batches: - pickings |= batch.picking_ids.filtered( - lambda picking: picking.state != "cancel" - ).sorted(lambda picking: (picking.partner_id.name or "", picking.name)) - return { - "doc_ids": docids, - "doc_model": "stock.picking.batch", - "docs": batches, - "pickings": pickings, - } diff --git a/stock_picking_batch_collect/report/report_batch_deliveryslip.xml b/stock_picking_batch_collect/report/report_batch_deliveryslip.xml deleted file mode 100644 index 91b6a74..0000000 --- a/stock_picking_batch_collect/report/report_batch_deliveryslip.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - Delivery Slips - stock.picking.batch - qweb-pdf - stock_picking_batch_collect.report_batch_deliveryslip - stock_picking_batch_collect.report_batch_deliveryslip - 'Delivery Slips - %s' % object.name - - report - - - - diff --git a/stock_picking_batch_collect/tests/__init__.py b/stock_picking_batch_collect/tests/__init__.py index ba5d668..a586ed8 100644 --- a/stock_picking_batch_collect/tests/__init__.py +++ b/stock_picking_batch_collect/tests/__init__.py @@ -1,3 +1,2 @@ from . import test_batch_summary # noqa: F401 from . import test_collected_picked # noqa: F401 -from . import test_picking_collected # noqa: F401 diff --git a/stock_picking_batch_collect/tests/test_picking_collected.py b/stock_picking_batch_collect/tests/test_picking_collected.py deleted file mode 100644 index 30bfbc3..0000000 --- a/stock_picking_batch_collect/tests/test_picking_collected.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright 2026 Criptomart -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import Command -from odoo.exceptions import UserError -from odoo.tests import TransactionCase -from odoo.tests import tagged - - -@tagged("-at_install", "post_install") -class TestPickingCollected(TransactionCase): - """Collected checks and ordered quantities on transfers outside a batch.""" - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.stock_location = cls.env.ref("stock.stock_location_stock") - cls.picking_type_out = cls.env.ref("stock.picking_type_out") - cls.picking_type_out.collected_check_required = True - cls.env.company.write( - { - "batch_detailed_restriction_enabled": True, - "batch_detailed_restriction_scope": "processed", - "batch_summary_restriction_enabled": False, - } - ) - kg = cls.env.ref("uom.product_uom_kgm") - cls.product = cls.env["product.product"].create( - { - "name": "Collected Pears", - "is_storable": True, - "uom_id": kg.id, - "uom_po_id": kg.id, - } - ) - cls.env["stock.quant"]._update_available_quantity( - cls.product, cls.stock_location, 50.0 - ) - cls.partner = cls.env["res.partner"].create({"name": "Pear Lover"}) - - def _create_sale_picking(self, qty=2.0): - order = self.env["sale.order"].create( - { - "partner_id": self.partner.id, - "order_line": [ - Command.create( - {"product_id": self.product.id, "product_uom_qty": qty} - ) - ], - } - ) - order.action_confirm() - picking = order.picking_ids - picking.action_assign() - return picking - - def test_loose_picking_blocks_until_collected(self): - picking = self._create_sale_picking() - - with self.assertRaisesRegex(UserError, self.product.display_name): - picking.button_validate() - - picking.move_ids.is_collected = True - picking.button_validate() - - self.assertEqual(picking.state, "done") - - def test_picking_type_without_check_is_not_blocked(self): - self.picking_type_out.collected_check_required = False - picking = self._create_sale_picking() - - picking.button_validate() - - self.assertEqual(picking.state, "done") - - def test_batch_picking_follows_the_batch_restriction(self): - """The batch summary flag is not required by the loose transfer check.""" - self.env.company.batch_summary_restriction_enabled = True - picking = self._create_sale_picking() - batch = self.env["stock.picking.batch"].create( - {"picking_ids": [Command.set(picking.ids)]} - ) - batch.action_confirm() - picking.move_ids.is_collected = True - - with self.assertRaisesRegex(UserError, "product summary"): - batch.action_done() - - def test_collecting_the_move_collects_its_lines(self): - picking = self._create_sale_picking() - move = picking.move_ids - self.assertFalse(move.is_collected) - - move.is_collected = True - - self.assertTrue(all(move.move_line_ids.mapped("is_collected"))) - self.assertTrue(move.picked) - - move.move_line_ids.is_collected = False - - self.assertFalse(move.is_collected) - - def test_ordered_quantity_survives_weighing(self): - picking = self._create_sale_picking(2.0) - move = picking.move_ids - - move.move_line_ids.write({"quantity": 1.5, "picked": True}) - - self.assertEqual(move.product_uom_qty, 1.5) - self.assertEqual(move.sale_ordered_qty, 2.0) - self.assertEqual(move.move_line_ids.sale_ordered_qty, 2.0) - - def test_batch_summary_ordered_quantity(self): - pickings = self._create_sale_picking(2.0) | self._create_sale_picking(3.0) - batch = self.env["stock.picking.batch"].create( - {"picking_ids": [Command.set(pickings.ids)]} - ) - pickings[0].move_ids.move_line_ids.write({"quantity": 1.5, "picked": True}) - - batch._compute_summary_line_ids() - - line = batch.summary_line_ids - self.assertEqual(line.qty_ordered, 5.0) - self.assertEqual(line.qty_demanded, 4.5) - - def test_report_prints_every_delivery_slip(self): - pickings = self._create_sale_picking(2.0) | self._create_sale_picking(3.0) - batch = self.env["stock.picking.batch"].create( - {"picking_ids": [Command.set(pickings.ids)]} - ) - - html, _report_format = self.env["ir.actions.report"]._render_qweb_html( - "stock_picking_batch_collect.report_batch_deliveryslip", batch.ids - ) - - for picking in pickings: - self.assertIn(picking.name, html.decode()) diff --git a/stock_picking_batch_collect/views/stock_picking_batch_views.xml b/stock_picking_batch_collect/views/stock_picking_batch_views.xml index 5918306..38c8d70 100644 --- a/stock_picking_batch_collect/views/stock_picking_batch_views.xml +++ b/stock_picking_batch_collect/views/stock_picking_batch_views.xml @@ -9,7 +9,6 @@ - @@ -31,7 +30,6 @@ context="{'display_default_code': False}"/> - @@ -80,9 +78,6 @@ - - - @@ -105,15 +100,4 @@ - - stock_picking_batch.picking.move.list.collect - stock.move - - - - - - - - diff --git a/stock_picking_batch_collect/views/stock_picking_views.xml b/stock_picking_batch_collect/views/stock_picking_views.xml deleted file mode 100644 index 80ef434..0000000 --- a/stock_picking_batch_collect/views/stock_picking_views.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - stock.picking.form.collect - stock.picking - - - - - - - - - - - - - stock.move.line.operations.list.collect - stock.move.line - - - - - - - - - - - - - stock.move.line.operations.list.popup.collect - stock.move.line - - - - - - - - - - stock.picking.type.form.collect - stock.picking.type - - - - - - - -