This commit is contained in:
gsinghpal
2026-05-18 22:33:23 -04:00
parent 25f568f225
commit 091f98e1f9
76 changed files with 4521 additions and 220 deletions

View File

@@ -292,7 +292,13 @@ class FusionShipment(models.Model):
# ── Tracking ──────────────────────────────────────────────
def action_refresh_tracking(self):
"""Fetch latest tracking events from Canada Post VIS API."""
"""Fetch latest tracking events from the carrier's API.
Dispatch by carrier_type:
- canada_post → Canada Post VIS API (inline below)
- fedex_rest → FedEx /track/v1/trackingnumbers
- other carriers → not yet supported; raise with clear message
"""
self.ensure_one()
if not self.tracking_number:
raise ValidationError(
@@ -301,6 +307,15 @@ class FusionShipment(models.Model):
if not carrier:
raise ValidationError(
_("No carrier linked to this shipment."))
if self.carrier_type == 'fedex_rest':
return self._refresh_tracking_fedex_rest()
if self.carrier_type != 'canada_post':
raise ValidationError(_(
"Refresh Tracking is only wired to Canada Post and "
"FedEx REST at this time. For %(carrier)s shipments, "
"use the Track Shipment button to view live tracking "
"on the carrier's website."
) % {'carrier': carrier.name})
# VIS tracking uses /vis/ path, not /rs/
if carrier.prod_environment:
@@ -745,3 +760,70 @@ class FusionShipment(models.Model):
attachment_ids=(
self.return_label_attachment_id.ids
if self.return_label_attachment_id else []))
# ── FedEx REST tracking ──────────────────────────────────────────
def _refresh_tracking_fedex_rest(self):
"""Call FedEx /track/v1/trackingnumbers and load scan events.
Parses the response via the FedexRestRequest.track_shipment
helper, replaces the shipment's tracking_event_ids with the
latest events, and updates status to 'delivered' if the latest
event indicates delivery. The 'delivered' transition cascades
to the portal_job via the existing write() hook.
"""
self.ensure_one()
from odoo.addons.fusion_shipping.api.fedex_rest.request import (
FedexRequest as FedexRestRequest,
)
try:
fedex = FedexRestRequest(self.carrier_id)
result = fedex.track_shipment(self.tracking_number)
except Exception as e:
raise ValidationError(
_("FedEx tracking error: %s") % str(e))
# Replace events.
self.tracking_event_ids.unlink()
vals_list = []
delivered = False
for evt in result.get('events') or []:
evt_date_str = ''
evt_time_str = ''
evt_datetime = False
raw_dt = evt.get('date_time') or ''
if raw_dt:
# FedEx returns ISO 8601 like 2026-05-18T14:30:00-05:00.
try:
parsed = dt_mod.fromisoformat(raw_dt)
# Strip tzinfo so it stores in Odoo's naive UTC fields.
if parsed.tzinfo is not None:
import pytz as _pytz
parsed = parsed.astimezone(_pytz.UTC).replace(tzinfo=None)
evt_datetime = parsed
evt_date_str = parsed.strftime('%Y-%m-%d')
evt_time_str = parsed.strftime('%H:%M:%S')
except (ValueError, TypeError):
pass
if (evt.get('event_type') or '').upper() == 'DL' or (
'delivered' in (evt.get('description') or '').lower()):
delivered = True
vals_list.append({
'shipment_id': self.id,
'event_date': evt_date_str or False,
'event_time': evt_time_str or '',
'event_datetime': evt_datetime,
'event_description': evt.get('description') or '',
'event_type': evt.get('event_type') or '',
'event_site': evt.get('city') or '',
'event_province': evt.get('state_province') or '',
'signatory_name': evt.get('signed_by') or '',
})
if vals_list:
self.env['fusion.tracking.event'].create(vals_list)
self.last_tracking_update = fields.Datetime.now()
if delivered and self.status != 'delivered':
self.status = 'delivered'
self.delivery_date = fields.Datetime.now()
self.message_post(body=_(
"FedEx tracking refreshed: %(n)d event(s) loaded. Status: %(s)s"
) % {'n': len(vals_list), 's': result.get('status') or ''})