1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/delivery_fedex/models/fedex_request.py
2024-12-10 09:04:09 +07:00

503 lines
27 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import binascii
import logging
import re
from datetime import datetime, date
from os.path import join as opj
from odoo.tools.zeep import Client, Plugin, Settings
from odoo.tools.zeep.exceptions import Fault
from odoo.tools.zeep.wsdl.utils import etree_to_string
from odoo.tools import remove_accents, float_repr
from odoo.tools.misc import file_path
_logger = logging.getLogger(__name__)
# uncomment to enable logging of Zeep requests and responses
# logging.getLogger('zeep.transports').setLevel(logging.DEBUG)
STATECODE_REQUIRED_COUNTRIES = ['US', 'CA', 'PR ', 'IN']
# Why using standardized ISO codes? It's way more fun to use made up codes...
# https://www.fedex.com/us/developer/WebHelp/ws/2014/dvg/WS_DVG_WebHelp/Appendix_F_Currency_Codes.htm
FEDEX_CURR_MATCH = {
u'UYU': u'UYP',
u'XCD': u'ECD',
u'MXN': u'NMP',
u'KYD': u'CID',
u'CHF': u'SFR',
u'GBP': u'UKL',
u'IDR': u'RPA',
u'DOP': u'RDD',
u'JPY': u'JYE',
u'KRW': u'WON',
u'SGD': u'SID',
u'CLP': u'CHP',
u'JMD': u'JAD',
u'KWD': u'KUD',
u'AED': u'DHS',
u'TWD': u'NTD',
u'ARS': u'ARN',
u'LVL': u'EURO',
}
class LogPlugin(Plugin):
""" Small plugin for zeep that catches out/ingoing XML requests and logs them"""
def __init__(self, debug_logger):
self.debug_logger = debug_logger
def egress(self, envelope, http_headers, operation, binding_options):
self.debug_logger(etree_to_string(envelope).decode(), 'fedex_request')
return envelope, http_headers
def ingress(self, envelope, http_headers, operation):
self.debug_logger(etree_to_string(envelope).decode(), 'fedex_response')
return envelope, http_headers
def marshalled(self, context):
context.envelope = context.envelope.prune()
class FedexRequest():
""" Low-level object intended to interface Odoo recordsets with FedEx,
through appropriate SOAP requests """
def __init__(self, debug_logger, request_type="shipping", prod_environment=False, ):
self.debug_logger = debug_logger
self.hasCommodities = False
wsdl_folder = 'prod' if prod_environment else 'test'
if request_type == "shipping":
wsdl_path = opj('delivery_fedex', 'api', wsdl_folder, 'ShipService_v28.wsdl')
self.start_shipping_transaction(wsdl_path)
elif request_type == "rating":
wsdl_path = opj('delivery_fedex', 'api', wsdl_folder, 'RateService_v31.wsdl')
self.start_rating_transaction(wsdl_path)
# Authentification stuff
def web_authentication_detail(self, key, password):
WebAuthenticationCredential = self.factory.WebAuthenticationCredential()
WebAuthenticationCredential.Key = key
WebAuthenticationCredential.Password = password
self.WebAuthenticationDetail = self.factory.WebAuthenticationDetail()
self.WebAuthenticationDetail.UserCredential = WebAuthenticationCredential
def transaction_detail(self, transaction_id):
self.TransactionDetail = self.factory.TransactionDetail()
self.TransactionDetail.CustomerTransactionId = transaction_id
def client_detail(self, account_number, meter_number):
self.ClientDetail = self.factory.ClientDetail()
self.ClientDetail.AccountNumber = account_number
self.ClientDetail.MeterNumber = meter_number
# Common stuff
def set_shipper(self, company_partner, warehouse_partner):
Contact = self.factory.Contact()
Contact.PersonName = remove_accents(company_partner.name) if not company_partner.is_company else ''
Contact.CompanyName = remove_accents(company_partner.commercial_company_name) or ''
Contact.PhoneNumber = warehouse_partner.phone or ''
Contact.EMailAddress = warehouse_partner.email or ''
# TODO fedex documentation asks for TIN number, but it seems to work without
Address = self.factory.Address()
Address.StreetLines = [remove_accents(warehouse_partner.street) or '',remove_accents(warehouse_partner.street2) or '']
Address.City = remove_accents(warehouse_partner.city) or ''
if warehouse_partner.country_id.code in STATECODE_REQUIRED_COUNTRIES:
Address.StateOrProvinceCode = warehouse_partner.state_id.code or ''
else:
Address.StateOrProvinceCode = ''
Address.PostalCode = warehouse_partner.zip or ''
Address.CountryCode = warehouse_partner.country_id.code or ''
self.RequestedShipment.Shipper = self.factory.Party()
self.RequestedShipment.Shipper.Contact = Contact
self.RequestedShipment.Shipper.Address = Address
def set_recipient(self, recipient_partner):
Contact = self.factory.Contact()
if recipient_partner.is_company:
Contact.PersonName = ''
Contact.CompanyName = remove_accents(recipient_partner.name)
else:
Contact.PersonName = remove_accents(recipient_partner.name)
Contact.CompanyName = remove_accents(recipient_partner.commercial_company_name) or ''
Contact.PhoneNumber = recipient_partner.phone or ''
Contact.EMailAddress = recipient_partner.email or ''
Address = self.factory.Address()
Address.StreetLines = [remove_accents(recipient_partner.street) or '', remove_accents(recipient_partner.street2) or '']
Address.City = remove_accents(recipient_partner.city) or ''
if recipient_partner.country_id.code in STATECODE_REQUIRED_COUNTRIES:
Address.StateOrProvinceCode = recipient_partner.state_id.code or ''
else:
Address.StateOrProvinceCode = ''
Address.PostalCode = recipient_partner.zip or ''
Address.CountryCode = recipient_partner.country_id.code or ''
self.RequestedShipment.Recipient = self.factory.Party()
self.RequestedShipment.Recipient.Contact = Contact
self.RequestedShipment.Recipient.Address = Address
def shipment_request(self, dropoff_type, service_type, packaging_type, overall_weight_unit, saturday_delivery):
self.RequestedShipment = self.factory.RequestedShipment()
self.RequestedShipment.SpecialServicesRequested = self.factory.ShipmentSpecialServicesRequested()
self.RequestedShipment.ShipTimestamp = datetime.now()
self.RequestedShipment.DropoffType = dropoff_type
self.RequestedShipment.ServiceType = service_type
self.RequestedShipment.PackagingType = packaging_type
# Resuest estimation of duties and taxes for international shipping
if service_type in ['INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY']:
self.RequestedShipment.EdtRequestType = 'ALL'
else:
self.RequestedShipment.EdtRequestType = 'NONE'
self.RequestedShipment.PackageCount = 0
self.RequestedShipment.TotalWeight = self.factory.Weight()
self.RequestedShipment.TotalWeight.Units = overall_weight_unit
self.RequestedShipment.TotalWeight.Value = 0
self.listCommodities = []
if saturday_delivery:
timestamp_day = self.RequestedShipment.ShipTimestamp.strftime("%A")
if (service_type == 'FEDEX_2_DAY' and timestamp_day == 'Thursday') or (service_type in ['PRIORITY_OVERNIGHT', 'FIRST_OVERNIGHT', 'INTERNATIONAL_PRIORITY'] and timestamp_day == 'Friday'):
self.RequestedShipment.SpecialServicesRequested.SpecialServiceTypes.append('SATURDAY_DELIVERY')
def set_currency(self, currency):
# set perferred currency as GBP instead of UKL
currency = 'GBP' if currency == 'UKL' else currency
self.RequestedShipment.PreferredCurrency = currency
# ask Fedex to include our preferred currency in the response
self.RequestedShipment.RateRequestTypes = 'PREFERRED'
def set_master_package(self, weight, package_count, master_tracking_id=False):
self.RequestedShipment.TotalWeight.Value = weight
self.RequestedShipment.PackageCount = package_count
if master_tracking_id:
self.RequestedShipment.MasterTrackingId = self.factory.TrackingId()
self.RequestedShipment.MasterTrackingId.TrackingIdType = 'FEDEX'
self.RequestedShipment.MasterTrackingId.TrackingNumber = master_tracking_id
# weight_value, package_code=False, package_height=0, package_width=0, package_length=0,
def add_package(self, carrier, delivery_package, fdx_company_currency, sequence_number=False, mode='shipping', po_number=False, dept_number=False, reference=False):
package = self.factory.RequestedPackageLineItem()
package_weight = self.factory.Weight()
package_weight.Value = carrier._fedex_convert_weight(delivery_package.weight, carrier.fedex_weight_unit)
package_weight.Units = self.RequestedShipment.TotalWeight.Units
package.PhysicalPackaging = 'BOX'
if delivery_package.packaging_type == 'YOUR_PACKAGING':
package.Dimensions = self.factory.Dimensions()
package.Dimensions.Height = int(delivery_package.dimension['height'])
package.Dimensions.Width = int(delivery_package.dimension['width'])
package.Dimensions.Length = int(delivery_package.dimension['length'])
# TODO in master, add unit in product packaging and perform unit conversion
package.Dimensions.Units = "IN" if self.RequestedShipment.TotalWeight.Units == 'LB' else 'CM'
if po_number:
po_reference = self.factory.CustomerReference()
po_reference.CustomerReferenceType = 'P_O_NUMBER'
po_reference.Value = po_number
package.CustomerReferences.append(po_reference)
if dept_number:
dept_reference = self.factory.CustomerReference()
dept_reference.CustomerReferenceType = 'DEPARTMENT_NUMBER'
dept_reference.Value = dept_number
package.CustomerReferences.append(dept_reference)
if reference:
customer_reference = self.factory.CustomerReference()
customer_reference.CustomerReferenceType = 'CUSTOMER_REFERENCE'
customer_reference.Value = reference
package.CustomerReferences.append(customer_reference)
if carrier.shipping_insurance:
package.InsuredValue = self.factory.Money()
insured_value = delivery_package.total_cost * carrier.shipping_insurance / 100
pkg_order = delivery_package.order_id or delivery_package.picking_id.sale_id
# Get the currency from the sale order if it exists, so that it matches that of customs_value
if pkg_order:
package.InsuredValue.Currency = _convert_curr_iso_fdx(pkg_order.currency_id.name)
package.InsuredValue.Amount = float_repr(delivery_package.company_id.currency_id._convert(insured_value, pkg_order.currency_id, pkg_order.company_id, date.today()), 2)
else:
package.InsuredValue.Currency = fdx_company_currency
package.InsuredValue.Amount = float_repr(insured_value, 2)
package.Weight = package_weight
if mode == 'rating':
package.GroupPackageCount = 1
if sequence_number:
package.SequenceNumber = sequence_number
if mode == 'rating':
self.RequestedShipment.RequestedPackageLineItems.append(package)
else:
self.RequestedShipment.RequestedPackageLineItems = package
# Rating stuff
def start_rating_transaction(self, wsdl_path):
settings = Settings(strict=False)
self.client = Client(file_path(wsdl_path), plugins=[LogPlugin(self.debug_logger)], settings=settings)
self.factory = self.client.type_factory('ns0')
self.VersionId = self.factory.VersionId()
self.VersionId.ServiceId = 'crs'
self.VersionId.Major = '31'
self.VersionId.Intermediate = '0'
self.VersionId.Minor = '0'
def rate(self, request):
formatted_response = {'price': {}}
try:
self.response = self.client.service.getRates(WebAuthenticationDetail=request['WebAuthenticationDetail'],
ClientDetail=request['ClientDetail'],
TransactionDetail=request['TransactionDetail'],
Version=request['VersionId'],
RequestedShipment=request['RequestedShipment'])
if (self.response.HighestSeverity != 'ERROR' and self.response.HighestSeverity != 'FAILURE'):
if not getattr(self.response, "RateReplyDetails", False):
raise Exception("No rating found")
for rating in self.response.RateReplyDetails[0].RatedShipmentDetails:
formatted_response['price'][rating.ShipmentRateDetail.TotalNetFedExCharge.Currency] = float(rating.ShipmentRateDetail.TotalNetFedExCharge.Amount)
if len(self.response.RateReplyDetails[0].RatedShipmentDetails) == 1:
if 'CurrencyExchangeRate' in self.response.RateReplyDetails[0].RatedShipmentDetails[0].ShipmentRateDetail and self.response.RateReplyDetails[0].RatedShipmentDetails[0].ShipmentRateDetail['CurrencyExchangeRate']:
formatted_response['price'][self.response.RateReplyDetails[0].RatedShipmentDetails[0].ShipmentRateDetail.CurrencyExchangeRate.FromCurrency] = float(self.response.RateReplyDetails[0].RatedShipmentDetails[0].ShipmentRateDetail.TotalNetFedExCharge.Amount) / float(self.response.RateReplyDetails[0].RatedShipmentDetails[0].ShipmentRateDetail.CurrencyExchangeRate.Rate)
else:
errors_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if (n.Severity == 'ERROR' or n.Severity == 'FAILURE')])
formatted_response['errors_message'] = errors_message
if any([n.Severity == 'WARNING' for n in self.response.Notifications]):
warnings_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if n.Severity == 'WARNING'])
formatted_response['warnings_message'] = warnings_message
except Fault as fault:
formatted_response['errors_message'] = fault
except IOError:
formatted_response['errors_message'] = "Fedex Server Not Found"
except Exception as e:
formatted_response['errors_message'] = e.args[0]
return formatted_response
# Shipping stuff
def start_shipping_transaction(self, wsdl_path):
self.client = Client(file_path(wsdl_path), plugins=[LogPlugin(self.debug_logger)])
self.factory = self.client.type_factory("ns0")
self.VersionId = self.factory.VersionId()
self.VersionId.ServiceId = 'ship'
self.VersionId.Major = '28'
self.VersionId.Intermediate = '0'
self.VersionId.Minor = '0'
def shipment_label(self, label_format_type, image_type, label_stock_type, label_printing_orientation, label_order):
LabelSpecification = self.factory.LabelSpecification()
LabelSpecification.LabelFormatType = label_format_type
LabelSpecification.ImageType = image_type
LabelSpecification.LabelStockType = label_stock_type
LabelSpecification.LabelPrintingOrientation = label_printing_orientation
LabelSpecification.LabelOrder = label_order
self.RequestedShipment.LabelSpecification = LabelSpecification
def commercial_invoice(self, document_stock_type, send_etd=False):
shipping_document = self.factory.ShippingDocumentSpecification()
shipping_document.ShippingDocumentTypes = "COMMERCIAL_INVOICE"
commercial_invoice_detail = self.factory.CommercialInvoiceDetail()
commercial_invoice_detail.Format = self.factory.ShippingDocumentFormat()
commercial_invoice_detail.Format.ImageType = "PDF"
commercial_invoice_detail.Format.StockType = document_stock_type
shipping_document.CommercialInvoiceDetail = commercial_invoice_detail
self.RequestedShipment.ShippingDocumentSpecification = shipping_document
if send_etd:
self.RequestedShipment.SpecialServicesRequested.SpecialServiceTypes.append('ELECTRONIC_TRADE_DOCUMENTS')
etd_details = self.factory.EtdDetail()
etd_details.RequestedDocumentCopies.append('COMMERCIAL_INVOICE')
self.RequestedShipment.SpecialServicesRequested.EtdDetail = etd_details
def shipping_charges_payment(self, shipping_charges_payment_account):
self.RequestedShipment.ShippingChargesPayment = self.factory.Payment()
self.RequestedShipment.ShippingChargesPayment.PaymentType = 'SENDER'
Payor = self.factory.Payor()
Payor.ResponsibleParty = self.factory.Party()
Payor.ResponsibleParty.AccountNumber = shipping_charges_payment_account
self.RequestedShipment.ShippingChargesPayment.Payor = Payor
def duties_payment(self, sender_party, responsible_account_number, payment_type):
self.RequestedShipment.CustomsClearanceDetail.DutiesPayment = self.factory.Payment()
self.RequestedShipment.CustomsClearanceDetail.DutiesPayment.PaymentType = payment_type
if payment_type == 'SENDER':
Payor = self.factory.Payor()
Payor.ResponsibleParty = self.factory.Party()
Payor.ResponsibleParty.Address = self.factory.Address()
Payor.ResponsibleParty.Address.CountryCode = sender_party.country_id.code
Payor.ResponsibleParty.AccountNumber = responsible_account_number
self.RequestedShipment.CustomsClearanceDetail.DutiesPayment.Payor = Payor
def customs_value(self, customs_value_currency, customs_value_amount, document_content):
self.RequestedShipment.CustomsClearanceDetail = self.factory.CustomsClearanceDetail()
if self.hasCommodities:
self.RequestedShipment.CustomsClearanceDetail.Commodities = self.listCommodities
self.RequestedShipment.CustomsClearanceDetail.CustomsValue = self.factory.Money()
self.RequestedShipment.CustomsClearanceDetail.CustomsValue.Currency = customs_value_currency
self.RequestedShipment.CustomsClearanceDetail.CustomsValue.Amount = float_repr(customs_value_amount, 2)
if self.RequestedShipment.Shipper.Address.CountryCode == "IN" and self.RequestedShipment.Recipient.Address.CountryCode == "IN":
if not self.RequestedShipment.CustomsClearanceDetail.CommercialInvoice:
self.RequestedShipment.CustomsClearanceDetail.CommercialInvoice = self.factory.CommercialInvoice()
else:
del self.RequestedShipment.CustomsClearanceDetail.CommercialInvoice.TaxesOrMiscellaneousChargeType
self.RequestedShipment.CustomsClearanceDetail.CommercialInvoice.Purpose = 'SOLD'
# Old keys not requested anymore but still in WSDL; not removing them causes crash
del self.RequestedShipment.CustomsClearanceDetail['ClearanceBrokerage']
del self.RequestedShipment.CustomsClearanceDetail['FreightOnValue']
self.RequestedShipment.CustomsClearanceDetail.DocumentContent = document_content
def commodities(self, carrier, delivery_commodity, commodity_currency):
self.hasCommodities = True
commodity = self.factory.Commodity()
commodity.UnitPrice = self.factory.Money()
commodity.UnitPrice.Currency = commodity_currency
commodity.UnitPrice.Amount = delivery_commodity.monetary_value
commodity.NumberOfPieces = '1'
commodity.CountryOfManufacture = delivery_commodity.country_of_origin
commodity_weight = self.factory.Weight()
commodity_weight.Value = carrier._fedex_convert_weight(delivery_commodity.product_id.weight * delivery_commodity.qty, carrier.fedex_weight_unit)
commodity_weight.Units = carrier.fedex_weight_unit
commodity.Weight = commodity_weight
commodity.Description = re.sub(r'[\[\]<>;={}"|]', '', delivery_commodity.product_id.name)
commodity.Quantity = delivery_commodity.qty
commodity.QuantityUnits = 'EA'
customs_value = self.factory.Money()
customs_value.Currency = commodity_currency
customs_value.Amount = delivery_commodity.monetary_value * delivery_commodity.qty
commodity.CustomsValue = customs_value
commodity.HarmonizedCode = delivery_commodity.product_id.hs_code.replace(".", "") if delivery_commodity.product_id.hs_code else ''
self.listCommodities.append(commodity)
def return_label(self, tracking_number, origin_date):
return_details = self.factory.ReturnShipmentDetail()
return_details.ReturnType = "PRINT_RETURN_LABEL"
if tracking_number and origin_date:
return_association = self.factory.ReturnAssociationDetail()
return_association.TrackingNumber = tracking_number
return_association.ShipDate = origin_date
return_details.ReturnAssociation = return_association
self.RequestedShipment.SpecialServicesRequested.SpecialServiceTypes.append("RETURN_SHIPMENT")
self.RequestedShipment.SpecialServicesRequested.ReturnShipmentDetail = return_details
if self.hasCommodities:
bla = self.factory.CustomsOptionDetail()
bla.Type = "FAULTY_ITEM"
self.RequestedShipment.CustomsClearanceDetail.CustomsOptions = bla
def process_shipment(self, request):
formatted_response = {'tracking_number': 0.0,
'price': {},
'master_tracking_id': None,
'date': None}
try:
self.response = self.client.service.processShipment(WebAuthenticationDetail=request['WebAuthenticationDetail'],
ClientDetail=request['ClientDetail'],
TransactionDetail=request['TransactionDetail'],
Version=request['VersionId'],
RequestedShipment=request['RequestedShipment'])
if (self.response.HighestSeverity != 'ERROR' and self.response.HighestSeverity != 'FAILURE'):
formatted_response['tracking_number'] = self.response.CompletedShipmentDetail.CompletedPackageDetails[0].TrackingIds[0].TrackingNumber
if 'CommitDate' in self.response.CompletedShipmentDetail.OperationalDetail:
formatted_response['date'] = self.response.CompletedShipmentDetail.OperationalDetail.CommitDate
else:
formatted_response['date'] = date.today()
if 'ShipmentRating' in self.response.CompletedShipmentDetail and self.response.CompletedShipmentDetail.ShipmentRating:
for rating in self.response.CompletedShipmentDetail.ShipmentRating.ShipmentRateDetails:
formatted_response['price'][rating.TotalNetFedExCharge.Currency] = float(rating.TotalNetFedExCharge.Amount)
if 'CurrencyExchangeRate' in rating and rating.CurrencyExchangeRate:
formatted_response['price'][rating.CurrencyExchangeRate.FromCurrency] = float(rating.TotalNetFedExCharge.Amount / rating.CurrencyExchangeRate.Rate)
else:
formatted_response['price']['USD'] = 0.0
if 'MasterTrackingId' in self.response.CompletedShipmentDetail:
formatted_response['master_tracking_id'] = self.response.CompletedShipmentDetail.MasterTrackingId.TrackingNumber
else:
errors_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if (n.Severity == 'ERROR' or n.Severity == 'FAILURE')])
formatted_response['errors_message'] = errors_message
if any([n.Severity == 'WARNING' for n in self.response.Notifications]):
warnings_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if n.Severity == 'WARNING'])
formatted_response['warnings_message'] = warnings_message
except Fault as fault:
formatted_response['errors_message'] = fault
except IOError:
formatted_response['errors_message'] = "Fedex Server Not Found"
return formatted_response
def _get_labels(self, file_type):
labels = [self.get_label()]
if file_type.upper() in ['PNG'] and self.response.CompletedShipmentDetail.CompletedPackageDetails[0].PackageDocuments:
for auxiliary in self.response.CompletedShipmentDetail.CompletedPackageDetails[0].PackageDocuments[0].Parts:
labels.append(auxiliary.Image)
return labels
def get_label(self):
return self.response.CompletedShipmentDetail.CompletedPackageDetails[0].Label.Parts[0].Image
def get_document(self):
if self.response.CompletedShipmentDetail.ShipmentDocuments:
return self.response.CompletedShipmentDetail.ShipmentDocuments[0].Parts[0].Image
else:
return False
# Deletion stuff
def set_deletion_details(self, tracking_number):
self.TrackingId = self.factory.TrackingId()
self.TrackingId.TrackingIdType = 'FEDEX'
self.TrackingId.TrackingNumber = tracking_number
self.DeletionControl = self.factory.DeletionControlType('DELETE_ALL_PACKAGES')
def delete_shipment(self, request):
formatted_response = {'delete_success': False}
try:
# Here, we send the Order 66
self.response = self.client.service.deleteShipment(WebAuthenticationDetail=request['WebAuthenticationDetail'],
ClientDetail=request['ClientDetail'],
TransactionDetail=request['TransactionDetail'],
Version=request['VersionId'],
TrackingId=request['TrackingId'],
DeletionControl=request['DeletionControl'])
if (self.response.HighestSeverity != 'ERROR' and self.response.HighestSeverity != 'FAILURE'):
formatted_response['delete_success'] = True
else:
errors_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if (n.Severity == 'ERROR' or n.Severity == 'FAILURE')])
formatted_response['errors_message'] = errors_message
if any([n.Severity == 'WARNING' for n in self.response.Notifications]):
warnings_message = '\n'.join([("%s: %s" % (n.Code, n.Message)) for n in self.response.Notifications if n.Severity == 'WARNING'])
formatted_response['warnings_message'] = warnings_message
except Fault as fault:
formatted_response['errors_message'] = fault
except IOError:
formatted_response['errors_message'] = "Fedex Server Not Found"
return formatted_response
def _convert_curr_fdx_iso(code):
curr_match = {v: k for k, v in FEDEX_CURR_MATCH.items()}
return curr_match.get(code, code)
def _convert_curr_iso_fdx(code):
return FEDEX_CURR_MATCH.get(code, code)