This commit is contained in:
gsinghpal
2026-04-29 03:35:33 -04:00
parent 6ac6d24da6
commit a2fe1fcbcc
61 changed files with 4655 additions and 667 deletions

View File

@@ -9,6 +9,7 @@ from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
# ===== Account hold (existing) ============================================
x_fc_account_hold = fields.Boolean(
string='Account Hold', tracking=True,
help='When active, blocks SO confirmation, invoicing, and shipping.',
@@ -20,3 +21,45 @@ class ResPartner(models.Model):
x_fc_account_hold_by_id = fields.Many2one(
'res.users', string='Hold Placed By',
)
# ===== Plating Defaults (cascade onto every new SO for this customer) =====
# The estimator sets these once on the customer record; they pre-fill
# invoice strategy, delivery method, and deadlines on every new SO so
# repeat customers don't need re-typing the same values each order.
# Tax type lives on `property_account_position_id` (Odoo native fiscal
# position) and payment terms on `property_payment_term_id` — both are
# surfaced on the same Plating Defaults tab in the partner form.
x_fc_default_invoice_strategy = fields.Selection(
[('deposit', 'Deposit'),
('progress', 'Progress Billing'),
('net_terms', 'Net Terms'),
('cod_prepay', 'COD / Prepay')],
string='Default Invoice Strategy',
help='Pre-fills the SO invoice strategy when this customer is selected. '
'The estimator can still override per order.',
)
x_fc_default_deposit_percent = fields.Float(
string='Default Deposit %',
help='Used when invoice strategy is "Deposit". e.g. 50.0 for 50%.',
)
x_fc_default_delivery_method = fields.Selection(
[('local_delivery', 'Local Delivery'),
('shipping_partner', 'Shipping Partner'),
('customer_pickup', 'Customer Pickup')],
string='Default Delivery Method',
help='Pre-fills the SO delivery method when this customer is selected.',
)
# Lead-time defaults are expressed as offsets FROM the SO's planned-start
# date so they track real production schedules, not just "today + N".
# If planned_start is unset on the SO, the cascade falls back to today.
x_fc_default_internal_deadline_days = fields.Integer(
string='Internal Deadline (+ days from start)',
help='Pre-fills SO internal deadline as planned_start_date + this '
'many days. e.g. 5 means "ship five days after we start".',
)
x_fc_default_customer_deadline_days = fields.Integer(
string='Customer Deadline (+ days from start)',
help='Pre-fills the customer-facing commitment date as '
'planned_start_date + this many days.',
)

View File

@@ -4,6 +4,7 @@
# Part of the Fusion Plating product family.
import logging
from datetime import timedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
@@ -16,16 +17,70 @@ class SaleOrder(models.Model):
@api.onchange('partner_id')
def _onchange_partner_id_invoice_strategy(self):
"""Auto-fill invoice strategy from customer defaults."""
if self.partner_id:
default = self.env['fp.invoice.strategy.default'].search(
[('partner_id', '=', self.partner_id.id)], limit=1,
)
if default:
self.x_fc_invoice_strategy = default.default_strategy
self.x_fc_deposit_percent = default.default_deposit_percent
if default.payment_term_id:
self.payment_term_id = default.payment_term_id
"""Auto-fill plating defaults from customer profile.
Cascade order: partner-level defaults first (the new fast-order
path), then fall back to the legacy fp.invoice.strategy.default
records for customers migrated before that model was retired.
Native Odoo cascades (payment terms, fiscal position) handle
themselves via property_* fields and don't need code here.
"""
if not self.partner_id:
return
partner = self.partner_id
if partner.x_fc_default_invoice_strategy:
self.x_fc_invoice_strategy = partner.x_fc_default_invoice_strategy
if partner.x_fc_default_deposit_percent:
self.x_fc_deposit_percent = partner.x_fc_default_deposit_percent
if partner.x_fc_default_delivery_method:
self.x_fc_delivery_method = partner.x_fc_default_delivery_method
self._fp_recompute_default_deadlines()
# Legacy fallback: invoice strategy default model. Only fills
# gaps left by the partner fields above so a partial migration
# doesn't clobber explicit partner-level values.
legacy = self.env['fp.invoice.strategy.default'].search(
[('partner_id', '=', partner.id)], limit=1,
)
if legacy:
if not self.x_fc_invoice_strategy:
self.x_fc_invoice_strategy = legacy.default_strategy
if not self.x_fc_deposit_percent:
self.x_fc_deposit_percent = legacy.default_deposit_percent
if legacy.payment_term_id and not self.payment_term_id:
self.payment_term_id = legacy.payment_term_id
@api.onchange('x_fc_planned_start_date')
def _onchange_planned_start_date_deadlines(self):
"""Recompute deadlines when planned start changes — without it
the partner offsets would only fire on partner_id change."""
self._fp_recompute_default_deadlines()
def _fp_recompute_default_deadlines(self):
"""Apply partner deadline offsets relative to planned_start_date.
Falls back to today when planned_start is unset so the estimator
gets a value immediately. Never overwrites a deadline already
set by the user (we honour explicit input over auto-fill).
"""
for order in self:
partner = order.partner_id
if not partner:
continue
anchor = order.x_fc_planned_start_date or fields.Date.context_today(order)
if (partner.x_fc_default_internal_deadline_days
and not order.x_fc_internal_deadline):
order.x_fc_internal_deadline = (
anchor + timedelta(days=partner.x_fc_default_internal_deadline_days)
)
if (partner.x_fc_default_customer_deadline_days
and not order.commitment_date):
order.commitment_date = (
anchor + timedelta(days=partner.x_fc_default_customer_deadline_days)
)
def action_confirm(self):
"""Override to check account hold + customer PO# and trigger