如何在 python-fedex 中为国际货件添加海关价值?

How to add customs value in python-fedex for international shipments?

我正在使用 python-fedex 模块作为 FedEx SOAP API 的轻包装。作为其中的一部分,我正在尝试设置一个国际货运的基本示例,但我遇到了以下错误消息:

fedex.base_service.FedexError: Customs Value is required. (Error code: 2033)

我认为我需要将我运送的产品添加为商品,包括。他们的海关价值 - 但我很难让它发挥作用。我在一些指导(来自 C#)的帮助下找到了 this link,但我无法让它在 Python 中工作。感谢任何输入!

我的代码如下:


# !/usr/bin/env python
"""
This example shows how to create a shipment and generate a waybill as output. The variables populated below
represents the minimum required values. You will need to fill all of these, or
risk seeing a SchemaValidationError exception thrown.

Near the bottom of the module, you'll see some different ways to handle the
label data that is returned with the reply.
"""

import logging
import binascii
import datetime
import sys, os

from example_config import CONFIG_OBJ

from fedex.services.ship_service import FedexProcessShipmentRequest


# What kind of file do you want this example to generate?
# Valid choices for this example are PDF, PNG
GENERATE_IMAGE_TYPE = 'PDF'

# Un-comment to see the response from Fedex printed in stdout.
logging.basicConfig(stream=sys.stdout, level=logging.INFO)

# This is the object that will be handling our shipment request.
# We're using the FedexConfig object from example_config.py in this dir.
customer_transaction_id = "*** ShipService Request v17 using Python ***"  # Optional transaction_id
shipment = FedexProcessShipmentRequest(CONFIG_OBJ, customer_transaction_id=customer_transaction_id)

# This is very generalized, top-level information.
# REGULAR_PICKUP, REQUEST_COURIER, DROP_BOX, BUSINESS_SERVICE_CENTER or STATION
shipment.RequestedShipment.DropoffType = 'BUSINESS_SERVICE_CENTER'

# See page 355 in WS_ShipService.pdf for a full list. Here are the common ones:
# STANDARD_OVERNIGHT, PRIORITY_OVERNIGHT, FEDEX_GROUND, FEDEX_EXPRESS_SAVER,
# FEDEX_2_DAY, INTERNATIONAL_PRIORITY, SAME_DAY, INTERNATIONAL_ECONOMY
shipment.RequestedShipment.ServiceType = 'INTERNATIONAL_PRIORITY'

# What kind of package this will be shipped in.
# FEDEX_BOX, FEDEX_PAK, FEDEX_TUBE, YOUR_PACKAGING, FEDEX_ENVELOPE
shipment.RequestedShipment.PackagingType = 'FEDEX_ENVELOPE'

# Shipper contact info.
shipment.RequestedShipment.Shipper.Contact.PersonName = 'Shipper Name'
shipment.RequestedShipment.Shipper.Contact.CompanyName = 'Shipper Company'
shipment.RequestedShipment.Shipper.Contact.PhoneNumber = '004512345678'

# Shipper address.
shipment.RequestedShipment.Shipper.Address.StreetLines = ['Shipper Address']
shipment.RequestedShipment.Shipper.Address.City = 'City'
shipment.RequestedShipment.Shipper.Address.StateOrProvinceCode = ''
shipment.RequestedShipment.Shipper.Address.PostalCode = '8270'
shipment.RequestedShipment.Shipper.Address.CountryCode = 'DK'
shipment.RequestedShipment.Shipper.Address.Residential = False

# Recipient contact info.
shipment.RequestedShipment.Recipient.Contact.PersonName = 'US customer X'
shipment.RequestedShipment.Recipient.Contact.CompanyName = 'US company X'
shipment.RequestedShipment.Recipient.Contact.PhoneNumber = '0123456789'

# Recipient address
shipment.RequestedShipment.Recipient.Address.StreetLines = ['668 MURRAY AVE SE']
shipment.RequestedShipment.Recipient.Address.City = 'ROANOKE'
shipment.RequestedShipment.Recipient.Address.StateOrProvinceCode = 'VA'
shipment.RequestedShipment.Recipient.Address.PostalCode = '24013'
shipment.RequestedShipment.Recipient.Address.CountryCode = 'US'
# This is needed to ensure an accurate rate quote with the response. Use AddressValidation to get ResidentialStatus
shipment.RequestedShipment.Recipient.Address.Residential = False
shipment.RequestedShipment.EdtRequestType = 'NONE'

# Senders account information
shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number

# Who pays for the shipment?
# RECIPIENT, SENDER or THIRD_PARTY
shipment.RequestedShipment.ShippingChargesPayment.PaymentType = 'SENDER'

# Specifies the label type to be returned.
# LABEL_DATA_ONLY or COMMON2D
shipment.RequestedShipment.LabelSpecification.LabelFormatType = 'COMMON2D'

# Specifies which format the label file will be sent to you in.
# DPL, EPL2, PDF, PNG, ZPLII
shipment.RequestedShipment.LabelSpecification.ImageType = GENERATE_IMAGE_TYPE

# To use doctab stocks, you must change ImageType above to one of the
# label printer formats (ZPLII, EPL2, DPL).
# See documentation for paper types, there quite a few.
shipment.RequestedShipment.LabelSpecification.LabelStockType = 'PAPER_7X4.75'

# This indicates if the top or bottom of the label comes out of the
# printer first.
# BOTTOM_EDGE_OF_TEXT_FIRST or TOP_EDGE_OF_TEXT_FIRST
# Timestamp in YYYY-MM-DDThh:mm:ss format, e.g. 2002-05-30T09:00:00
shipment.RequestedShipment.ShipTimestamp = datetime.datetime.now().replace(microsecond=0).isoformat()

# BOTTOM_EDGE_OF_TEXT_FIRST, TOP_EDGE_OF_TEXT_FIRST
shipment.RequestedShipment.LabelSpecification.LabelPrintingOrientation = 'TOP_EDGE_OF_TEXT_FIRST'

# Delete the flags we don't want.
# Can be SHIPPING_LABEL_FIRST, SHIPPING_LABEL_LAST or delete
if hasattr(shipment.RequestedShipment.LabelSpecification, 'LabelOrder'):
    del shipment.RequestedShipment.LabelSpecification.LabelOrder  # Delete, not using.

# Create Weight, in pounds.
package1_weight = shipment.create_wsdl_object_of_type('Weight')
package1_weight.Value = 1.0
package1_weight.Units = "LB"

# Create PackageLineItem
package1 = shipment.create_wsdl_object_of_type('RequestedPackageLineItem')
# BAG, BARREL, BASKET, BOX, BUCKET, BUNDLE, CARTON, CASE, CONTAINER, ENVELOPE etc..
package1.PhysicalPackaging = 'ENVELOPE'
package1.Weight = package1_weight


# Add a signature option for the package using SpecialServicesRequested or comment out.
# SpecialServiceTypes can be APPOINTMENT_DELIVERY, COD, DANGEROUS_GOODS, DRY_ICE, SIGNATURE_OPTION etc..
package1.SpecialServicesRequested.SpecialServiceTypes = 'SIGNATURE_OPTION'
# SignatureOptionType can be ADULT, DIRECT, INDIRECT, NO_SIGNATURE_REQUIRED, SERVICE_DEFAULT
package1.SpecialServicesRequested.SignatureOptionDetail.OptionType = 'SERVICE_DEFAULT'


# This adds the RequestedPackageLineItem WSDL object to the shipment. It
# increments the package count and total weight of the shipment for you.
shipment.add_package(package1)


# If you want to make sure that all of your entered details are valid, you
# can call this and parse it just like you would via send_request(). If
# shipment.response.HighestSeverity == "SUCCESS", your shipment is valid.
# print(shipment.send_validation_request())

# Fires off the request, sets the 'response' attribute on the object.
shipment.send_request()

您需要添加更多信息。

shipment.send_request() 之前插入以下代码,然后重试。

shipment.RequestedShipment.CustomsClearanceDetail.CustomsValue.Currency = 'USD'
shipment.RequestedShipment.CustomsClearanceDetail.CustomsValue.Amount = 1.0
shipment.RequestedShipment.CustomsClearanceDetail.DutiesPayment.PaymentType = 'SENDER'
shipment.RequestedShipment.CustomsClearanceDetail.DutiesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number

commodity1 = shipment.create_wsdl_object_of_type('Commodity')
commodity1.Name = 'book'
commodity1.NumberOfPieces = 1
commodity1.Description = '1'
commodity1.CountryOfManufacture = 'CN'
commodity1.HarmonizedCode = '123456789'

commodity1.Quantity = 1.0
commodity1.QuantityUnits = 'EA'

commodity1.Weight.Value = 1.0
commodity1.Weight.Units = "LB"

commodity1.CustomsValue.Currency = 'USD'
commodity1.CustomsValue.Amount = 1.0

commodity1.UnitPrice.Currency = 'USD'
commodity1.UnitPrice.Amount = 1.0

shipment.RequestedShipment.CustomsClearanceDetail.Commodities = [commodity1]

我在下面添加了一个完整的国际运输示例,解决了这个问题:

"""
This example shows how to create an international shipment and generate a waybill as output.
The example takes outset in a real practical use case, where electronic trade documents are
used and an existing PDF commercial invoice is added along with product descriptions via ETD.
Further, it adds event notifications to allow for emails to be sent to the end recipient.
The script is comprised of a FedExLabelHelper class with all core functions, and a use case
example with minimal dummy data
"""
from example_config import CONFIG_OBJ
from pathlib import Path
import binascii
import datetime
from fedex.services.ship_service import FedexProcessShipmentRequest

# ----------------------------------------------------
# FedEx class for creating shipments
class FedexLabelHelper:
    mCommodities = []

    def __init__(self):
        pass

    # ----------------------------------------------------
    # set overall shipment configuration
    def setShipmentConfig(
        self,
        CONFIG_OBJ,
        invoice_info,
        cust_tran_id="*** ShipService Request v17 using Python ***",
        dropoffType="BUSINESS_SERVICE_CENTER",
        shippingPaymentType="SENDER",
        labelFormatType="COMMON2D",
        labelSpecificationImageType="PDF",
        labelSpecificationStockType="PAPER_7X4.75",
        labelPrintingOrientation="TOP_EDGE_OF_TEXT_FIRST",
        LabelOrder="SHIPPING_LABEL_FIRST",
    ):
        self.invoice_info = invoice_info
        self.dropoffType = dropoffType
        self.serviceType = "INTERNATIONAL_PRIORITY" if invoice_info["ShippingExpress"] == True else "INTERNATIONAL_ECONOMY"
        self.mCommodities.clear()
        self.CONFIG_OBJ = CONFIG_OBJ
        self.shipment = FedexProcessShipmentRequest(CONFIG_OBJ, customer_transaction_id=cust_tran_id)

        self.shipment.RequestedShipment.DropoffType = dropoffType
        self.shipment.RequestedShipment.ServiceType = self.serviceType
        self.shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number
        self.shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.Address.CountryCode = "DK"
        self.shipment.RequestedShipment.ShippingChargesPayment.PaymentType = shippingPaymentType

        labelSpecification = self.shipment.create_wsdl_object_of_type("LabelSpecification")
        labelSpecification.LabelFormatType = labelFormatType
        labelSpecification.LabelStockType = labelSpecificationStockType
        labelSpecification.ImageType = labelSpecificationImageType
        labelSpecification.LabelOrder = LabelOrder
        labelSpecification.LabelPrintingOrientation = labelPrintingOrientation
        self.shipment.RequestedShipment.LabelSpecification = labelSpecification

    # ----------------------------------------------------
    # set sender information
    def setSenderInfo(self, sender):

        self.shipment.RequestedShipment.Shipper.Contact.PersonName = sender["Name"]
        self.shipment.RequestedShipment.Shipper.Contact.CompanyName = sender["Company"]
        self.shipment.RequestedShipment.Shipper.Contact.PhoneNumber = sender["Phone"]
        self.shipment.RequestedShipment.Shipper.Contact.EMailAddress = sender["Email"]
        self.shipment.RequestedShipment.Shipper.Address.StreetLines = sender["Address"]
        self.shipment.RequestedShipment.Shipper.Address.City = sender["City"]
        self.shipment.RequestedShipment.Shipper.Address.StateOrProvinceCode = sender["Region"]
        self.shipment.RequestedShipment.Shipper.Address.PostalCode = sender["Zip"]
        self.shipment.RequestedShipment.Shipper.Address.CountryCode = sender["CountryCode"]
        self.shipment.RequestedShipment.Shipper.Address.Residential = sender["Residential"]

        ti = self.shipment.create_wsdl_object_of_type("TaxpayerIdentification")
        ti.Number = sender["VAT"]
        ti.TinType = "BUSINESS_NATIONAL"
        self.shipment.RequestedShipment.Shipper.Tins = ti

    # ----------------------------------------------------
    # upload all documents (invoice and product information)
    def upload_all_documents(self):
        doc_ids = []
        doc_ids.append(self.upload_document(self.invoice_info["InvoicePath"], "COMMERCIAL_INVOICE"))

        for pdf in self.invoice_info["Pdfs"]:
            doc_ids.append(self.upload_document(pdf, "OTHER"))

        return doc_ids

    # ----------------------------------------------------
    # function for uploading documents as electronic trade documents and getting the response doc IDs
    def upload_document(self, path, type):
        from fedex.services.document_service import FedexDocumentServiceRequest

        # specify prefix for use in attachment naming
        if type == "COMMERCIAL_INVOICE":
            prefix = "invoice_"
        else:
            prefix = "product_description_"

        uploadRequest = FedexDocumentServiceRequest(self.CONFIG_OBJ)
        uploadRequest.OriginCountryCode = "DK"
        uploadRequest.DestinationCountryCode = self.shipment.RequestedShipment.Recipient.Address.CountryCode
        uploadRequest.Usage = "ELECTRONIC_TRADE_DOCUMENTS"

        clientdetails = uploadRequest.create_wsdl_object_of_type("ClientDetail")
        clientdetails.AccountNumber = self.CONFIG_OBJ.account_number
        clientdetails.MeterNumber = self.CONFIG_OBJ.meter_number
        uploadRequest.ClientDetail = clientdetails

        webAuthDetails = uploadRequest.create_wsdl_object_of_type("WebAuthenticationDetail")
        webAuthDetails.ParentCredential.Key = self.CONFIG_OBJ.key
        webAuthDetails.ParentCredential.Password = self.CONFIG_OBJ.password
        webAuthDetails.UserCredential.Key = self.CONFIG_OBJ.key
        webAuthDetails.UserCredential.Password = self.CONFIG_OBJ.password
        uploadRequest.WebAuthenticationDetail = webAuthDetails

        docdetails = uploadRequest.create_wsdl_object_of_type("UploadDocumentDetail")
        docdetails.LineNumber = 1
        docdetails.DocumentType = type
        docdetails.FileName = prefix + path
        fileContent = open(path, "rb").read()
        fileBase64 = binascii.b2a_base64(fileContent)
        docdetails.DocumentContent = fileBase64.decode("cp1250")
        uploadRequest.Documents = docdetails

        uploadRequest.send_request()

        doc_id = uploadRequest.response.DocumentStatuses[0].DocumentId

        return doc_id

    # ----------------------------------------------------
    # set recipient information
    def setRecipientInfo(self, recipient):
        self.shipment.RequestedShipment.Recipient.Contact.PersonName = recipient["Name"]
        self.shipment.RequestedShipment.Recipient.Contact.CompanyName = recipient["Company"]
        self.shipment.RequestedShipment.Recipient.Contact.PhoneNumber = recipient["Phone"]
        self.shipment.RequestedShipment.Recipient.Contact.EMailAddress = recipient["Email"]
        self.shipment.RequestedShipment.Recipient.Address.StreetLines = recipient["Address"]
        self.shipment.RequestedShipment.Recipient.Address.City = recipient["City"]
        self.shipment.RequestedShipment.Recipient.Address.StateOrProvinceCode = recipient["Region"]
        self.shipment.RequestedShipment.Recipient.Address.PostalCode = recipient["Zip"]
        self.shipment.RequestedShipment.Recipient.Address.CountryCode = recipient["CountryCode"]
        self.shipment.RequestedShipment.Recipient.Address.Residential = recipient["Residential"]

        ti = self.shipment.create_wsdl_object_of_type("TaxpayerIdentification")
        ti.Number = recipient["VAT"]
        ti.TinType = "BUSINESS_NATIONAL"
        self.shipment.RequestedShipment.Recipient.Tins = ti

    # ----------------------------------------------------
    # add "commercial invoice" reference as the only commodity
    def add_ci_commodity(self):

        self.addCommodity(
            cCustomsValueAmnt=self.invoice_info["Value"],
            cCustomsValueCurrency=self.invoice_info["Currency"],
            cWeightValue=self.invoice_info["Weight"],
            cDescription="See attached commercial invoice",
            cQuantity=self.invoice_info["Quantity"],
            cExportLicenseNumber=self.shipment.RequestedShipment.Shipper.Tins.Number,
            cPartNumber=1,
        )

    # ----------------------------------------------------
    # add commodity to shipment (for now, just add 1 commodity to refer to attached CI)
    def addCommodity(
        self, cCustomsValueAmnt, cCustomsValueCurrency, cWeightValue, cDescription, cQuantity, cExportLicenseNumber, cPartNumber,
    ):

        commodity = self.shipment.create_wsdl_object_of_type("Commodity")
        commodity.NumberOfPieces = str(cQuantity)
        commodity.Description = cDescription
        commodity.Quantity = cQuantity
        commodity.QuantityUnits = "EA"
        commodity.ExportLicenseNumber = cExportLicenseNumber
        commodity.PartNumber = cPartNumber
        commodity.CountryOfManufacture = "DK"

        mCustomsValue = self.shipment.create_wsdl_object_of_type("Money")
        mCustomsValue.Amount = cCustomsValueAmnt
        mCustomsValue.Currency = cCustomsValueCurrency
        commodity.CustomsValue = mCustomsValue

        commodity_weight = self.shipment.create_wsdl_object_of_type("Weight")
        commodity_weight.Value = cWeightValue
        commodity_weight.Units = "KG"
        commodity.Weight = commodity_weight

        munitPrice = self.shipment.create_wsdl_object_of_type("Money")
        munitPrice.Amount = float(round((cCustomsValueAmnt / cQuantity), 2))
        munitPrice.Currency = cCustomsValueCurrency
        commodity.UnitPrice = munitPrice

        self.mCommodities.append(commodity)

    # ----------------------------------------------------
    # add package to shipment
    def set_packaging_info(self):
        weight = self.invoice_info["Weight"]

        type = "BOX" if weight > 0.5 else "ENVELOPE"
        weight_final = float(round(weight + 0.2, 2)) if weight > 0.5 else 0.4

        self.addShippingPackage(packageWeight=weight_final, physicalPackagingType=type, packagingType=f"FEDEX_{type}")

    # ----------------------------------------------------
    # add package to shipment
    def addShippingPackage(self, packageWeight, physicalPackagingType, packagingType, packageWeightUnit="KG"):
        package_weight = self.shipment.create_wsdl_object_of_type("Weight")
        package_weight.Value = packageWeight
        package_weight.Units = packageWeightUnit

        package = self.shipment.create_wsdl_object_of_type("RequestedPackageLineItem")
        package.PhysicalPackaging = physicalPackagingType
        package.Weight = package_weight

        self.shipment.add_package(package)
        self.shipment.RequestedShipment.TotalWeight = package_weight
        self.shipment.RequestedShipment.PackagingType = packagingType

    # ----------------------------------------------------
    # add information on duties
    def setDutiesPaymentInfo(self):
        mParty = self.shipment.create_wsdl_object_of_type("Party")
        mParty.AccountNumber = self.CONFIG_OBJ.account_number
        mParty.Address = self.shipment.RequestedShipment.Recipient.Address

        mPayor = self.shipment.create_wsdl_object_of_type("Payor")
        mPayor.ResponsibleParty = mParty

        mPayment = self.shipment.create_wsdl_object_of_type("Payment")
        mPayment.PaymentType = "RECIPIENT"  # change if sender should pay duties
        mPayment.Payor = mPayor

        mCustomsValue = self.shipment.create_wsdl_object_of_type("Money")
        mCustomsValue.Amount = self.invoice_info["Value"]
        mCustomsValue.Currency = self.invoice_info["Currency"]

        ccd = self.shipment.create_wsdl_object_of_type("CustomsClearanceDetail")
        ccd.Commodities = self.mCommodities
        ccd.CustomsValue = mCustomsValue
        ccd.DutiesPayment = mPayment
        self.shipment.RequestedShipment.CustomsClearanceDetail = ccd

    # ----------------------------------------------------
    # Set ETD (electronic trade documents) settings
    def setSpecialServices(self, doc_ids):
        # construct objects
        ssr = self.shipment.create_wsdl_object_of_type("ShipmentSpecialServicesRequested")
        ssr.SpecialServiceTypes.append("ELECTRONIC_TRADE_DOCUMENTS")
        ssr.SpecialServiceTypes.append("EVENT_NOTIFICATION")

        # set up ETD details
        etd = self.shipment.create_wsdl_object_of_type("EtdDetail")
        etd.RequestedDocumentCopies = "COMMERCIAL INVOICE"

        for i, doc_id in enumerate(doc_ids, start=0):
            udrd = self.shipment.create_wsdl_object_of_type("UploadDocumentReferenceDetail")
            udrd.DocumentType = "COMMERCIAL_INVOICE" if i == 0 else "OTHER"
            udrd.DocumentId = doc_id
            udrd.Description = "Commercial_Invoice" if i == 0 else "Product_Description"
            udrd.DocumentIdProducer = "CUSTOMER"
            ssr.EtdDetail.DocumentReferences.append(udrd)

        self.shipment.RequestedShipment.SpecialServicesRequested = ssr

        # set Event Notification details
        send = self.shipment.create_wsdl_object_of_type("ShipmentEventNotificationDetail")
        send.AggregationType = "PER_SHIPMENT"

        sens = self.shipment.create_wsdl_object_of_type("ShipmentEventNotificationSpecification")
        sens.NotificationDetail.NotificationType = "EMAIL"
        sens.NotificationDetail.EmailDetail.EmailAddress = self.shipment.RequestedShipment.Recipient.Contact.EMailAddress
        sens.NotificationDetail.EmailDetail.Name = self.shipment.RequestedShipment.Recipient.Contact.PersonName
        sens.NotificationDetail.Localization.LanguageCode = "EN"
        sens.Role = "SHIPPER"
        sens.Events.append("ON_SHIPMENT")
        sens.Events.append("ON_EXCEPTION")
        sens.Events.append("ON_DELIVERY")
        sens.FormatSpecification.Type = "HTML"
        send.EventNotifications = sens
        self.shipment.RequestedShipment.SpecialServicesRequested.EventNotificationDetail = send

    # ----------------------------------------------------
    # process the shipment
    def processInternationalShipment(self):
        from shutil import copyfile

        self.shipment.RequestedShipment.ShipTimestamp = datetime.datetime.now().replace(microsecond=0).isoformat()

        # print(" ---- **** DETAILS ---- ****")
        # print(self.shipment.RequestedShipment)
        # print(self.shipment.ClientDetail)
        # print(self.shipment.TransactionDetail)
        # print("REQUESTED SHIPMENT\n\n", self.shipment.RequestedShipment)

        self.shipment.send_request()

        # print("RESPONSE\n\n", self.shipment.response)

        status = self.shipment.response.HighestSeverity

        if status == "SUCCESS" and "CompletedShipmentDetail" in self.shipment.response:
            shipment_details = self.shipment.response.CompletedShipmentDetail
            package_details = shipment_details.CompletedPackageDetails[0]
            tracking_id = package_details.TrackingIds[0].TrackingNumber
            email = self.shipment.RequestedShipment.Recipient.Contact.EMailAddress
            fedex_cost = "N/A"

            if hasattr(package_details, "PackageRating"):
                fedex_cost = package_details.PackageRating.PackageRateDetails[0].NetCharge.Amount

            # create the shipping PDF label
            ascii_label_data = package_details.Label.Parts[0].Image
            label_binary_data = binascii.a2b_base64(ascii_label_data)
            out_path = self.invoice_info["InvoiceId"] + f"_shipment_label_{tracking_id}.pdf"

            out_file = open(out_path, "wb")
            out_file.write(label_binary_data)
            out_file.close()

            # print output information
            print(
                f"- SUCCESS: Created FedEx label for invoice {self.invoice_info['InvoiceId']}\n     tracking ID: {tracking_id}\n     email: {email}\n     FedEx cost: {fedex_cost}\n     Customs value: {self.invoice_info['Value']} {self.invoice_info['Currency']}\n     Weight: {self.invoice_info['Weight']}\n     output path: {out_path}"
            )


# ----------------------------------------------------
# main script
commercial_invoice_path = "commercial_invoice_test.pdf"
product_description_1_path = "product_description_test.pdf"

sender = {
    "Company": "Sender Company",
    "Name": "Mr Smith",
    "Address": ["Address 1", "Address 2"],
    "Region": "",
    "Zip": "8230",
    "City": "Abyhoj",
    "Country": "Denmark",
    "Phone": "12345678",
    "Email": "mail@mail.com",
    "CountryCode": "DK",
    "Currency": "EUR",
    "VAT": "DK12345678",
    "Residential": False,
}

recipient = {
    "Company": "Recipient Co",
    "Name": "Contact Name",
    "Address": ["Adr1, Adr2"],
    "Region": "MN",
    "Zip": "55420",
    "City": "Bloomington",
    "Country": "United States",
    "Phone": "0123456789",
    "Email": "mail@mail.com",
    "CountryCode": "US",
    "Currency": "EUR",
    "VAT": "",
    "Residential": False,
}

invoice_info = {
    "InvoiceId": "14385",
    "Weight": 0.11,
    "Quantity": 2,
    "Value": 20.0,
    "Shipping": 25.0,
    "ShippingExpress": True,
    "Currency": "EUR",
    "InvoicePath": commercial_invoice_path,
    "Pdfs": [product_description_1_path],
}

# print output
print(f"\n- recipient: {recipient}\n- invoice_info: {invoice_info}\n")

# create FedEx Label Helper and set configuration
flh = FedexLabelHelper()
flh.setShipmentConfig(CONFIG_OBJ=CONFIG_OBJ, invoice_info=invoice_info)

# add sender & recipient info to FedEx shipment
flh.setSenderInfo(sender)
flh.setRecipientInfo(recipient)

# set packaging based on weight
flh.set_packaging_info()

# add reference to CI as only commodity info
flh.add_ci_commodity()

# set duties payment information
flh.setDutiesPaymentInfo()

# upload documents
doc_ids = flh.upload_all_documents()

# link uploaded documents as ETD and setup event notifications
flh.setSpecialServices(doc_ids)

# process shipments and create shipping labels
flh.processInternationalShipment()