changes
This commit is contained in:
@@ -165,7 +165,21 @@ class FedexRequest:
|
||||
def _process_errors(self, res_body):
|
||||
err_msgs = []
|
||||
for err in res_body.get('errors', []):
|
||||
err_msgs.append(f"{err['message']} ({err['code']})")
|
||||
msg = f"{err.get('message', '')} ({err.get('code', '')})"
|
||||
# FedEx hides the specific field-validation failures in
|
||||
# parameterList (e.g. INVALID.INPUT.EXCEPTION's top-level
|
||||
# message is just "Validation failed for object='X'. Error
|
||||
# count: 1" — the actual field name lives in parameterList).
|
||||
# Surface them so operators see "city cannot be null" instead
|
||||
# of a useless generic exception.
|
||||
params = err.get('parameterList') or []
|
||||
details = '; '.join(
|
||||
f"{p.get('key', '')}={p.get('value', '')}"
|
||||
for p in params if p.get('key') or p.get('value')
|
||||
)
|
||||
if details:
|
||||
msg += f"\n {details}"
|
||||
err_msgs.append(msg)
|
||||
return ','.join(err_msgs)
|
||||
|
||||
def _process_alerts(self, response):
|
||||
@@ -395,7 +409,8 @@ class FedexRequest:
|
||||
self._strip_customs_for_domestic(request_data)
|
||||
res = self._send_fedex_request("/rate/v1/rates/quotes", request_data)
|
||||
try:
|
||||
rate = next(filter(lambda d: d['currency'] == fedex_currency, res['rateReplyDetails'][0]['ratedShipmentDetails']), {})
|
||||
reply = res['rateReplyDetails'][0]
|
||||
rate = next(filter(lambda d: d['currency'] == fedex_currency, reply['ratedShipmentDetails']), {})
|
||||
if rate.get('totalNetChargeWithDutiesAndTaxes', 0):
|
||||
price = rate['totalNetChargeWithDutiesAndTaxes']
|
||||
else:
|
||||
@@ -403,8 +418,22 @@ class FedexRequest:
|
||||
except KeyError:
|
||||
raise ValidationError(_('Could not decode response')) from None
|
||||
|
||||
# Commit info — service display name + estimated delivery date
|
||||
# for the receiving form's shipping-quote preview panel.
|
||||
# FedEx returns several shapes depending on service; fall
|
||||
# through gracefully so callers that only need `price` still
|
||||
# work.
|
||||
commit = reply.get('commit') or {}
|
||||
date_detail = commit.get('dateDetail') or {}
|
||||
return {
|
||||
'price': price,
|
||||
'currency': fedex_currency,
|
||||
'service_type': reply.get('serviceType') or self.service_type,
|
||||
'service_name': reply.get('serviceName') or '',
|
||||
'delivery_timestamp': commit.get('deliveryTimestamp')
|
||||
or date_detail.get('dayCxsFormat') or '',
|
||||
'day_of_week': commit.get('dayOfWeek') or '',
|
||||
'transit_time': commit.get('transitTime') or '',
|
||||
'alert_message': self._process_alerts(res),
|
||||
}
|
||||
|
||||
|
||||
@@ -2459,13 +2459,29 @@ class DeliveryCarrier(models.Model):
|
||||
def fusion_fedex_rest_send_shipping(self, pickings):
|
||||
res = []
|
||||
srm = FedexRestRequest(self)
|
||||
# Per-shipment service override — fp.receiving sets this on the
|
||||
# carrier via with_context() before calling send_shipping. Empty
|
||||
# falls back to the carrier-level default already on srm.
|
||||
# See CLAUDE.md "Per-shipment service override".
|
||||
override = self.env.context.get('fp_service_type_override')
|
||||
if override:
|
||||
srm.service_type = override
|
||||
for picking in pickings:
|
||||
packages = self._get_packages_from_picking(picking, self.fedex_rest_default_package_type_id)
|
||||
# SoldTo defaults to the SO's invoice partner, but many setups
|
||||
# leave the parent contact (used as invoice fallback) with a
|
||||
# name-only record and no address — FedEx rejects on `soldTo.
|
||||
# address.city cannot be null`. If the invoice partner has no
|
||||
# city, treat ship-to as sold-to so _ship_package skips the
|
||||
# soldTo block entirely (line guard: `if sold_to != ship_to`).
|
||||
invoice_partner = picking.sale_id.partner_invoice_id
|
||||
if not (invoice_partner and invoice_partner.city):
|
||||
invoice_partner = picking.partner_id
|
||||
response = srm._ship_package(
|
||||
ship_from_wh=picking.picking_type_id.warehouse_id.partner_id,
|
||||
ship_from_company=picking.company_id.partner_id,
|
||||
ship_to=picking.partner_id,
|
||||
sold_to=picking.sale_id.partner_invoice_id,
|
||||
sold_to=invoice_partner,
|
||||
packages=packages,
|
||||
currency=picking.sale_id.currency_id.name or picking.company_id.currency_id.name,
|
||||
order_no=picking.sale_id.name,
|
||||
|
||||
@@ -267,10 +267,22 @@ class FusionShipment(models.Model):
|
||||
}
|
||||
|
||||
def _action_open_attachment(self, attachment):
|
||||
"""Open an attachment PDF in the browser viewer (new tab)."""
|
||||
"""Open an attachment for the operator.
|
||||
|
||||
Delegates to ir.attachment.action_fusion_preview — PDFs render
|
||||
in the preview dialog, anything else (ZPL, etc.) downloads.
|
||||
Helper falls back gracefully when fusion_pdf_preview isn't
|
||||
installed. See CLAUDE.md "PDF Preview" for the contract.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if not attachment:
|
||||
return False
|
||||
if hasattr(attachment, 'action_fusion_preview'):
|
||||
return attachment.action_fusion_preview(
|
||||
title=attachment.name or 'Shipping Label',
|
||||
model_name=self._name,
|
||||
record_ids=self.id,
|
||||
)
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': '/web/content/%s?download=false' % attachment.id,
|
||||
|
||||
Reference in New Issue
Block a user