[IMP] stock_picking_batch_collect: collected checks on transfers, ordered quantity, delivery slips report
- The collected checks also apply to transfers outside a batch, opt-in per
operation type ("Require Collected Lines"), with the batch restrictions of
the settings. A per-product toggle in the transfer's Operations tab collects
all its lines.
- Read-only ordered quantity from the sale line on every batch and transfer
view, since the weighed demand overwrites it. Adds a dependency on
sale_stock; a migration fills it in the summary of open batches.
- "Delivery Slips" report on the batch: all its delivery slips in one PDF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
76dc355c6c
commit
9c81cb986d
24 changed files with 630 additions and 39 deletions
|
|
@ -2,6 +2,7 @@
|
|||
# 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):
|
||||
|
|
@ -19,21 +20,68 @@ class StockPicking(models.Model):
|
|||
if res is not True:
|
||||
return res
|
||||
|
||||
self._check_batch_collected_products()
|
||||
self._check_collected_products()
|
||||
return res
|
||||
|
||||
def _check_batch_collected_products(self):
|
||||
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):
|
||||
for batch in self.batch_id:
|
||||
batch_pickings = self.filtered(
|
||||
lambda picking, batch=batch: picking.batch_id == batch
|
||||
)
|
||||
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 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,
|
||||
)
|
||||
if not processed_move_lines:
|
||||
continue
|
||||
batch._check_all_products_collected(batch_pickings)
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue