cron better exception management. show supplier commercial name in product card template. linters config
This commit is contained in:
parent
a812b4385d
commit
87dcb4e350
17 changed files with 104 additions and 100 deletions
|
|
@ -10,9 +10,6 @@ from odoo import models
|
|||
from odoo.exceptions import ValidationError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
# Pylint: explicit 'string' attributes are intentional for readable labels in views.
|
||||
# Some linters flag these as redundant; disable that specific check here.
|
||||
# pylint: disable=attribute-string-redundant
|
||||
|
||||
|
||||
class GroupOrder(models.Model):
|
||||
|
|
@ -945,6 +942,7 @@ class GroupOrder(models.Model):
|
|||
# route configuration doesn't block all remaining orders.
|
||||
confirmed_sale_orders = SaleOrder.browse()
|
||||
failed_sale_orders = SaleOrder.browse()
|
||||
failure_reasons = {}
|
||||
|
||||
for sale_order in sale_orders:
|
||||
try:
|
||||
|
|
@ -953,9 +951,18 @@ class GroupOrder(models.Model):
|
|||
# route issues. This cron confirms business orders first
|
||||
# and handles stock exceptions operationally.
|
||||
sale_order.with_context(from_orderpoint=True).action_confirm()
|
||||
confirmed_sale_orders |= sale_order
|
||||
except Exception:
|
||||
# Flush INSIDE the savepoint. Odoo raises many
|
||||
# ValidationErrors lazily at flush/recompute time, which
|
||||
# would otherwise escape this savepoint (it has already
|
||||
# been released on block exit) and poison the
|
||||
# transaction, aborting every remaining sale order. By
|
||||
# forcing the flush here, a failing order is rolled back
|
||||
# to its savepoint and the loop continues cleanly.
|
||||
self.env.flush_all()
|
||||
confirmed_sale_orders |= sale_order
|
||||
except Exception as exc:
|
||||
failed_sale_orders |= sale_order
|
||||
failure_reasons[sale_order.id] = str(exc) or exc.__class__.__name__
|
||||
_logger.exception(
|
||||
"Cron: Error confirming sale order %s (%s) for group order %s (%s). "
|
||||
"Order skipped; remaining orders will continue.",
|
||||
|
|
@ -965,9 +972,12 @@ class GroupOrder(models.Model):
|
|||
self.name,
|
||||
)
|
||||
|
||||
batches = self.env["stock.picking.batch"]
|
||||
if confirmed_sale_orders:
|
||||
# Create picking batches only for confirmed sale orders
|
||||
self._create_picking_batches_for_sale_orders(confirmed_sale_orders)
|
||||
batches = self._create_picking_batches_for_sale_orders(
|
||||
confirmed_sale_orders
|
||||
)
|
||||
self._log_missing_procurement_warnings(confirmed_sale_orders)
|
||||
|
||||
if failed_sale_orders:
|
||||
|
|
@ -980,6 +990,9 @@ class GroupOrder(models.Model):
|
|||
self.name,
|
||||
failed_sale_orders.ids,
|
||||
)
|
||||
self._post_failed_sale_orders_message(
|
||||
batches, failed_sale_orders, failure_reasons
|
||||
)
|
||||
except Exception:
|
||||
_logger.exception(
|
||||
"Cron: Error confirming sale orders for group order %s (%s)",
|
||||
|
|
@ -987,6 +1000,66 @@ class GroupOrder(models.Model):
|
|||
self.name,
|
||||
)
|
||||
|
||||
def _post_failed_sale_orders_message(
|
||||
self, batches, failed_sale_orders, failure_reasons=None
|
||||
):
|
||||
"""Post a note on the picking batch wall about sale orders that failed.
|
||||
|
||||
Operators read the batch chatter to follow up manually on orders that
|
||||
could not be confirmed (and are therefore NOT part of the batch).
|
||||
|
||||
Args:
|
||||
batches: stock.picking.batch recordset to notify (may be empty).
|
||||
failed_sale_orders: sale.order recordset that failed to confirm.
|
||||
failure_reasons: optional {sale_order_id: error message} mapping.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not failed_sale_orders:
|
||||
return
|
||||
|
||||
failure_reasons = failure_reasons or {}
|
||||
from markupsafe import Markup
|
||||
from markupsafe import escape
|
||||
|
||||
items = Markup()
|
||||
for sale_order in failed_sale_orders:
|
||||
reason = failure_reasons.get(sale_order.id)
|
||||
label = sale_order.name or self.env._("(unnamed)")
|
||||
partner = sale_order.partner_id.display_name or ""
|
||||
item = escape(f"{label} ({partner})") if partner else escape(label)
|
||||
if reason:
|
||||
item += Markup(": ") + escape(reason)
|
||||
items += Markup("<li>%s</li>") % item
|
||||
|
||||
body = (
|
||||
escape(
|
||||
self.env._(
|
||||
"%(count)d sale order(s) linked to group order "
|
||||
"“%(name)s” failed to confirm and are NOT part of this "
|
||||
"batch. Please review and confirm them manually:",
|
||||
count=len(failed_sale_orders),
|
||||
name=self.name,
|
||||
)
|
||||
)
|
||||
+ Markup("<ul>%s</ul>") % items
|
||||
)
|
||||
|
||||
if not batches:
|
||||
# No batch was created (all confirmations failed), so there is no
|
||||
# wall to post on. The warning logged by the caller is the record.
|
||||
_logger.warning(
|
||||
"Cron: No picking batch available to report %d failed sale "
|
||||
"orders for group order %s (%s): ids=%s",
|
||||
len(failed_sale_orders),
|
||||
self.id,
|
||||
self.name,
|
||||
failed_sale_orders.ids,
|
||||
)
|
||||
return
|
||||
|
||||
for batch in batches:
|
||||
batch.message_post(body=body, subtype_xmlid="mail.mt_note")
|
||||
|
||||
def _log_missing_procurement_warnings(self, sale_orders):
|
||||
"""Log warnings for confirmed orders with stockable lines lacking moves.
|
||||
|
||||
|
|
@ -1028,9 +1101,13 @@ class GroupOrder(models.Model):
|
|||
|
||||
Args:
|
||||
sale_orders: Recordset of confirmed sale.order
|
||||
|
||||
Returns:
|
||||
stock.picking.batch recordset with the batches created here.
|
||||
"""
|
||||
self.ensure_one()
|
||||
StockPickingBatch = self.env["stock.picking.batch"].sudo()
|
||||
created_batches = StockPickingBatch.browse()
|
||||
|
||||
# Create batches per group order, not per consumer group.
|
||||
# If multiple picking types exist, keep one batch per picking type.
|
||||
|
|
@ -1067,6 +1144,7 @@ class GroupOrder(models.Model):
|
|||
)
|
||||
|
||||
pickings.write({"batch_id": batch.id})
|
||||
created_batches |= batch
|
||||
|
||||
_logger.info(
|
||||
"Cron: Created batch %s with %d pickings for group order %s",
|
||||
|
|
@ -1074,3 +1152,5 @@ class GroupOrder(models.Model):
|
|||
len(pickings),
|
||||
self.name,
|
||||
)
|
||||
|
||||
return created_batches
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue