Files
Odoo-Modules/fusion_plating/fusion_plating_receiving/models/fusion_shipment.py
gsinghpal 091f98e1f9 changes
2026-05-18 22:33:23 -04:00

137 lines
5.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""Phase C — extend fusion.shipment with dimension fields.
fusion_shipping's native model has `weight` but no length/width/height.
The plating workflow needs all four captured at receiving time so the
shipment record carries everything the carrier API would want. Added
here (not in fusion_shipping) to keep the upstream module untouched.
"""
import logging
from odoo import api, fields, models
_logger = logging.getLogger(__name__)
class FusionShipment(models.Model):
_inherit = 'fusion.shipment'
x_fc_length = fields.Float(string='Length', digits=(10, 2))
x_fc_width = fields.Float(string='Width', digits=(10, 2))
x_fc_height = fields.Float(string='Height', digits=(10, 2))
x_fc_dim_uom = fields.Selection(
[('in', 'in'), ('cm', 'cm')],
string='Dim UoM', default='in',
)
x_fc_weight_uom = fields.Selection(
[('lb', 'lb'), ('kg', 'kg')],
string='Weight UoM', default='lb',
)
# Multi-piece label storage. label_attachment_id remains the
# primary (first box) for backward-compat; this M2M holds the full
# set so the operator can download any box's label individually.
x_fc_label_attachment_ids = fields.Many2many(
'ir.attachment',
'fusion_shipment_label_attachment_rel',
'shipment_id', 'attachment_id',
string='All Labels',
copy=False,
)
# Phase C — resolved carrier tracking URL with the tracking number
# substituted into the carrier.tracking_url template. Used by the
# shipment_labeled email template and any other place that needs a
# working clickable tracking link. Single source of truth so both
# email + portal stay consistent.
x_fc_tracking_url = fields.Char(
string='Tracking URL (resolved)',
compute='_compute_x_fc_tracking_url',
help='carrier.tracking_url with <shipmenttrackingnumber> replaced '
'by tracking_number. Empty when the carrier has no URL '
'template or there is no tracking number yet.',
)
@api.depends('carrier_id.tracking_url', 'tracking_number')
def _compute_x_fc_tracking_url(self):
for rec in self:
tpl = (rec.carrier_id.tracking_url or '') if rec.carrier_id else ''
tn = rec.tracking_number or ''
if not tpl or not tn:
rec.x_fc_tracking_url = ''
continue
placeholder = '<shipmenttrackingnumber>'
if placeholder in tpl:
rec.x_fc_tracking_url = tpl.replace(placeholder, tn)
else:
rec.x_fc_tracking_url = tpl + tn
def write(self, vals):
"""Sync the carrier tracking number + label to the customer
portal job whenever they land on the shipment. The portal_job
currently shows `delivery.name` as 'tracking' — wrong; the
customer wants the carrier's actual tracking number so the
clickable link goes to FedEx/UPS/etc."""
res = super().write(vals)
sync_keys = {'tracking_number', 'label_attachment_id', 'status'}
if not sync_keys & set(vals.keys()):
return res
for ship in self:
try:
ship._fp_sync_to_portal_job()
except Exception as e:
_logger.warning(
'Shipment %s: portal-job sync failed: %s',
ship.name, e,
)
return res
def _fp_sync_to_portal_job(self):
"""Walk shipment → SO → fp.job → fusion.plating.portal.job
and push the carrier tracking number + label + delivery's
packing slip onto the customer-facing record.
"""
self.ensure_one()
if not self.sale_order_id:
return
Job = self.env.get('fp.job')
if Job is None:
return
jobs = Job.sudo().search(
[('sale_order_id', '=', self.sale_order_id.id)],
)
if not jobs:
return
for job in jobs:
portal = job.portal_job_id
if not portal:
continue
vals = {}
if self.tracking_number and portal.tracking_ref != self.tracking_number:
vals['tracking_ref'] = self.tracking_number
# Packing slip lives on the linked fp.delivery, not the
# shipment. Walk it lazily here so a packing-slip generated
# earlier on the delivery also lands on the portal job.
delivery = job.delivery_id
if (delivery
and 'packing_list_attachment_id' in delivery._fields
and delivery.packing_list_attachment_id
and portal.packing_list_attachment_id !=
delivery.packing_list_attachment_id):
vals['packing_list_attachment_id'] = (
delivery.packing_list_attachment_id.id
)
# Once a tracking number exists, the parts have been picked
# by the carrier (or are about to be) — advance the portal
# state to 'shipped' so the customer sees their order is
# on its way. The 'delivered' status flips when FedEx
# tracking reports the delivery.
if self.tracking_number and portal.state in (
'received', 'in_progress', 'ready_to_ship',
):
vals['state'] = 'shipped'
if vals:
portal.sudo().write(vals)