-
+
+
+
+
+
+ Order will confirm without a PO number or
+ document. A chase activity will be scheduled
+ for the expected date so sales follows up
+ with the customer.
+
diff --git a/fusion_plating/fusion_plating_invoicing/__manifest__.py b/fusion_plating/fusion_plating_invoicing/__manifest__.py
index dc4fb64d..6a1e73c2 100644
--- a/fusion_plating/fusion_plating_invoicing/__manifest__.py
+++ b/fusion_plating/fusion_plating_invoicing/__manifest__.py
@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Invoicing',
- 'version': '19.0.2.3.0',
+ 'version': '19.0.3.0.0',
'category': 'Manufacturing/Plating',
'summary': 'Invoice strategy engine with deposit, progress billing, net terms, COD/prepay, and account holds.',
'description': """
diff --git a/fusion_plating/fusion_plating_invoicing/models/sale_order.py b/fusion_plating/fusion_plating_invoicing/models/sale_order.py
index 6f677af0..546ff245 100644
--- a/fusion_plating/fusion_plating_invoicing/models/sale_order.py
+++ b/fusion_plating/fusion_plating_invoicing/models/sale_order.py
@@ -31,23 +31,36 @@ class SaleOrder(models.Model):
"""Override to check account hold + customer PO# and trigger
the invoice strategy."""
for order in self:
- # --- Customer PO# required ---
+ # --- Customer PO# required (with escape hatches) ---
# Aerospace AP teams reject invoices without their PO#
# quoted back. Catching this at SO confirm prevents the
# whole downstream chain (CoC, BoL, invoice) from going
# out unreferenced. The PO# is on `client_order_ref`
# (Odoo standard) AND mirrored to `x_fc_po_number`
# (FP-specific) — accept either as filled.
+ #
+ # Two escape hatches for real-world cases:
+ # * x_fc_po_pending — estimator flag: "PO coming later".
+ # Confirms the order without a PO number and schedules
+ # a chase activity for x_fc_po_expected_date so sales
+ # doesn't forget to chase the paperwork.
+ # * x_fc_po_override — manager waiver: "proceed without
+ # a formal PO (handshake deal)". Permanent.
po_set = bool(order.client_order_ref) or bool(
getattr(order, 'x_fc_po_number', False)
)
- if not po_set:
+ po_pending = bool(getattr(order, 'x_fc_po_pending', False))
+ po_override = bool(getattr(order, 'x_fc_po_override', False))
+ if not po_set and not po_pending and not po_override:
raise UserError(_(
'Cannot confirm SO "%(so)s" — Customer PO# is required.\n\n'
'Set the customer\'s purchase order number in the '
'"Customer Reference" field (or x_fc_po_number) before '
- 'confirming. Aerospace customers\' AP teams reject '
- 'invoices that don\'t quote their PO# back.'
+ 'confirming. If the customer will provide the PO '
+ 'later, tick "PO Pending" and set "PO Expected By" — '
+ 'the order will confirm with a follow-up activity to '
+ 'chase the paperwork. A Plating Manager can also set '
+ '"PO Override" for handshake deals.'
) % {'so': order.name})
# --- Account hold check ---
@@ -73,6 +86,36 @@ class SaleOrder(models.Model):
res = super().action_confirm()
+ # --- PO-pending chase activity ---
+ # Sales team needs to chase the customer for the real PO before
+ # any invoice goes out. Schedule a follow-up on the expected
+ # date (or 3 days out if unset).
+ for order in self:
+ if not getattr(order, 'x_fc_po_pending', False):
+ continue
+ if getattr(order, 'x_fc_po_number', False) or order.client_order_ref:
+ continue # PO already set — nothing to chase
+ from datetime import timedelta
+ expected = (
+ order.x_fc_po_expected_date
+ or (fields.Date.context_today(order) + timedelta(days=3))
+ )
+ order.activity_schedule(
+ 'mail.mail_activity_data_todo',
+ date_deadline=expected,
+ summary=_('Chase customer PO for %s') % order.name,
+ note=_(
+ 'Order confirmed with PO Pending. Follow up with '
+ '%(partner)s for their PO number (and PDF if '
+ 'available), then tick PO Pending off and enter the '
+ 'PO# on the sale order.'
+ ) % {'partner': order.partner_id.display_name},
+ )
+ order.message_post(body=_(
+ 'Order confirmed without PO. Chase activity scheduled '
+ 'for %(date)s.'
+ ) % {'date': expected.strftime('%Y-%m-%d')})
+
# --- Invoice strategy automation (on confirm) ---
for order in self:
strategy = order.x_fc_invoice_strategy