Example #1
0
def shipment_request(payload: ShipmentRequest,
                     settings: Settings) -> Serializable:

    if payload.shipper.country_code is not None and payload.shipper.country_code != Country.US.name:
        raise OriginNotServicedError(payload.shipper.country_code)

    if payload.recipient.country_code == Country.US.name:
        raise DestinationNotServicedError(payload.recipient.country_code)

    service = ServiceType[payload.service]

    # Create a First Class Mail Shipment Request
    if service == ServiceType.usps_first_class_mail_international:
        return first_class_mail.shipment_request(payload, settings)

    # Create a GXG Shipment Request
    elif service == ServiceType.usps_global_express_guaranteed:
        return global_express_guaranteed.shipment_request(payload, settings)

    # Create a Priority Mail Shipment Request
    elif service == ServiceType.usps_priority_mail_international:
        return priority_mail.shipment_request(payload, settings)

    # Fallback to creating a Priority Express Mail Shipment Request
    else:
        return priority_express.shipment_request(payload, settings)
Example #2
0
def rate_request(payload: RateRequest,
                 settings: Settings) -> Serializable[RateV4Request]:
    """Create the appropriate USPS rate request depending on the destination

    :param payload: Purplship unified API rate request data
    :param settings: USPS connection and auth settings
    :return: a domestic or international USPS compatible request
    :raises: an OriginNotServicedError when origin country is not serviced by the carrier
    """

    if payload.shipper.country_code is not None and payload.shipper.country_code != Country.US.name:
        raise OriginNotServicedError(payload.shipper.country_code)

    if payload.recipient.country_code is not None and payload.recipient.country_code == Country.US.name:
        raise DestinationNotServicedError(payload.recipient.country_code)

    package = Packages(payload.parcels).single
    options = Options(payload.options, ShipmentOption)
    service = (Services(payload.services, ShipmentService).first
               or ShipmentService.usps_all)
    special_services = [
        getattr(option, 'value', option) for key, option in options
        if 'usps_option' not in key
    ]
    insurance = next(
        (option.value for key, option in options if 'usps_insurance' in key),
        options.insurance)

    container = PackagingType[package.packaging_type or "your_packaging"]
    sort_level = (SortLevelType[container.name].value
                  if service.value in ["All", "Online"] else None)
    mail_type = (FirstClassMailType[container.name].value
                 if 'first_class' in service.value else None)

    request = RateV4Request(
        USERID=settings.username,
        Revision="2",
        Package=[
            PackageType(
                ID=0,
                Service=service.value,
                FirstClassMailType=mail_type,
                ZipOrigination=payload.shipper.postal_code,
                ZipDestination=payload.recipient.postal_code,
                Pounds=package.weight.LB,
                Ounces=package.weight.OZ,
                Container=container.value,
                Width=package.width.IN,
                Length=package.length.IN,
                Height=package.height.IN,
                Girth=package.girth.value,
                Value=insurance,
                AmountToCollect=options.cash_on_delivery,
                SpecialServices=(SpecialServicesType(
                    SpecialService=[s for s in special_services])
                                 if any(special_services) else None),
                Content=None,
                GroundOnly=options.usps_option_ground_only,
                SortBy=sort_level,
                Machinable=(options.usps_option_machinable_item or False),
                ReturnLocations=options.usps_option_return_service_info,
                ReturnServiceInfo=options.usps_option_return_service_info,
                DropOffTime=('13:30'
                             if options.shipment_date is not None else None),
                ShipDate=(ShipDateType(
                    valueOf_=DF.fdate(options.shipment_date))
                          if options.shipment_date is not None else None),
            )
        ],
    )

    return Serializable(request, XP.export)
Example #3
0
def rate_request(payload: RateRequest, settings: Settings) -> Serializable[IntlRateV2Request]:
    """Create the appropriate USPS International rate request depending on the destination

    :param payload: Purplship unified API rate request data
    :param settings: USPS International connection and auth settings
    :return: a domestic or international USPS International compatible request
    :raises:
        - OriginNotServicedError when origin country is not serviced by the carrier
        - DestinationNotServicedError when destination country is US
    """

    if payload.shipper.country_code is not None and payload.shipper.country_code != Country.US.name:
        raise OriginNotServicedError(payload.shipper.country_code)

    if payload.recipient.country_code == Country.US.name:
        raise DestinationNotServicedError(payload.recipient.country_code)

    package = Packages(payload.parcels, max_weight=Weight(70, WeightUnit.LB)).single
    options = Options(payload.options, ShipmentOption)
    services = Services(payload.services, ShipmentService)

    extra_services = [getattr(option, 'value', option) for key, option in options if 'usps_option' not in key]
    commercial = next(("Y" for svc in services if "commercial" in svc.name), "N")
    commercial_plus = next(("Y" for svc in services if "plus" in svc.name), "N")
    country = (
        Country[payload.recipient.country_code].value
        if payload.recipient.country_code else None
    )

    request = IntlRateV2Request(
        USERID=settings.username,
        Revision="2",
        Package=[
            PackageType(
                ID=0,
                Pounds=package.weight.LB,
                Ounces=package.weight.OZ,
                Machinable=options["usps_option_machinable_item"],
                MailType=PackagingType[package.packaging_type or "package"].value,
                GXG=None,
                ValueOfContents=None,
                Country=country,
                Width=package.width.IN,
                Length=package.length.IN,
                Height=package.height.IN,
                Girth=(package.girth.value if package.packaging_type == "tube" else None),
                OriginZip=payload.shipper.postal_code,
                CommercialFlag=commercial,
                CommercialPlusFlag=commercial_plus,
                AcceptanceDateTime=datetime.today().strftime("%Y-%m-%dT%H:%M:%S"),
                DestinationPostalCode=payload.recipient.postal_code,
                ExtraServices=(
                    ExtraServicesType(
                        ExtraService=[
                            getattr(option, 'value', option)
                            for option in extra_services
                        ]
                    ) if any(extra_services) else None
                ),
                Content=None,
            )
        ],
    )

    return Serializable(request, XP.export)
Example #4
0
def rate_request(payload: RateRequest,
                 settings: Settings) -> Serializable[DCTRequest]:
    packages = Packages(payload.parcels, PackagePresets, required=["weight"])
    products = [*Services(payload.services, ProductCode)]
    options = Options(payload.options, SpecialServiceCode)

    is_international = payload.shipper.country_code != payload.recipient.country_code
    is_document = all([parcel.is_document for parcel in payload.parcels])
    is_dutiable = not is_document

    if not is_international and payload.shipper.country_code in ["CA"]:
        raise DestinationNotServicedError(payload.shipper.country_code)

    paperless = (SpecialServiceCode.dhl_paperless_trade if
                 (is_international and is_dutiable) else None)
    special_services = [
        *options, *([(paperless.name, None)] if paperless is not None else [])
    ]
    insurance = options[
        'dhl_shipment_insurance'].value if 'dhl_shipment_insurance' in options else None

    if len(products) == 0:
        if is_international and is_document:
            product = 'dhl_express_worldwide_doc'
        elif is_international:
            product = 'dhl_express_worldwide_nondoc'
        elif is_document:
            product = 'dhl_express_worldwide_doc'
        else:
            product = 'dhl_express_easy_nondoc'

        products = [ProductCode[product]]

    request = DCTRequest(GetQuote=GetQuoteType(
        Request=settings.Request(
            MetaData=MetaData(SoftwareName="3PV", SoftwareVersion=1.0)),
        From=DCTFrom(
            CountryCode=payload.shipper.country_code,
            Postalcode=payload.shipper.postal_code,
            City=payload.shipper.city,
            Suburb=payload.shipper.state_code,
        ),
        To=DCTTo(
            CountryCode=payload.recipient.country_code,
            Postalcode=payload.recipient.postal_code,
            City=payload.recipient.city,
            Suburb=payload.recipient.state_code,
        ),
        BkgDetails=BkgDetailsType(
            PaymentCountryCode=payload.shipper.country_code,
            NetworkTypeCode=NetworkType.both_time_and_day_definite.value,
            WeightUnit=WeightUnit.LB.value,
            DimensionUnit=DimensionUnit.IN.value,
            ReadyTime=time.strftime("PT%HH%MM"),
            Date=time.strftime("%Y-%m-%d"),
            IsDutiable=("Y" if is_dutiable else "N"),
            Pieces=PiecesType(Piece=[
                PieceType(
                    PieceID=package.parcel.id or f"{index}",
                    PackageTypeCode=DCTPackageType[package.packaging_type
                                                   or "your_packaging"].value,
                    Depth=package.length.IN,
                    Width=package.width.IN,
                    Height=package.height.IN,
                    Weight=package.weight.LB,
                ) for index, package in enumerate(
                    cast(Iterable[Package], packages), 1)
            ]),
            NumberOfPieces=len(packages),
            ShipmentWeight=packages.weight.LB,
            Volume=None,
            PaymentAccountNumber=settings.account_number,
            InsuredCurrency=(options.currency
                             if insurance is not None else None),
            InsuredValue=insurance,
            PaymentType=None,
            AcctPickupCloseTime=None,
            QtdShp=[
                QtdShpType(
                    GlobalProductCode=product.value,
                    LocalProductCode=product.value,
                    QtdShpExChrg=[
                        QtdShpExChrgType(
                            SpecialServiceType=SpecialServiceCode[key].value.
                            key) for key, _ in special_services
                        if key in SpecialServiceCode
                    ],
                ) for product in products
            ],
        ),
        Dutiable=(DCTDutiable(
            DeclaredValue=insurance or 1.0,
            DeclaredCurrency=options.currency
            or CountryCurrency[payload.shipper.country_code].value)
                  if is_international and is_dutiable else None),
    ), )

    return Serializable(request, _request_serializer)
Example #5
0
def shipment_request(payload: ShipmentRequest,
                     settings: Settings) -> Serializable[eVSRequest]:
    if payload.shipper.country_code is not None and payload.shipper.country_code != Country.US.name:
        raise OriginNotServicedError(payload.shipper.country_code)

    if payload.recipient.country_code is not None and payload.recipient.country_code != Country.US.name:
        raise DestinationNotServicedError(payload.recipient.country_code)

    service = ServiceType[payload.service].value
    package = Packages(payload.parcels).single
    options = Options(payload.options, ShipmentOption)

    customs = payload.customs or Customs(commodities=[])
    extra_services = [
        getattr(option, 'value', option) for key, option in options
        if 'usps_option' not in key
    ]
    label_format = LabelFormat[payload.label_type or 'usps_6_x_4_label'].value
    insurance = next(
        (option.value for key, option in options if 'usps_insurance' in key),
        options.insurance)
    # Gets the first provided non delivery option or default to "RETURN"
    non_delivery = next(
        (option.value for name, option in options if 'non_delivery' in name),
        "RETURN")
    redirect_address = Address(
        **(options['usps_option_redirect_non_delivery'] or {}))

    request = eVSRequest(
        USERID=settings.username,
        Option=None,
        Revision="1",
        ImageParameters=ImageParametersType(ImageParameter=label_format,
                                            LabelSequence=LabelSequenceType(
                                                PackageNumber=1,
                                                TotalPackages=1)),
        FromName=payload.shipper.person_name,
        FromFirm=payload.shipper.company_name or "N/A",
        FromAddress1=payload.shipper.address_line1,
        FromAddress2=payload.shipper.address_line2,
        FromCity=payload.shipper.city,
        FromState=payload.shipper.state_code,
        FromZip5=Location(payload.shipper.postal_code).as_zip5,
        FromZip4=Location(payload.shipper.postal_code).as_zip4,
        FromPhone=payload.shipper.phone_number,
        POZipCode=None,
        AllowNonCleansedOriginAddr=False,
        ToName=payload.recipient.person_name,
        ToFirm=payload.recipient.company_name or "N/A",
        ToAddress1=payload.recipient.address_line1,
        ToAddress2=payload.recipient.address_line2,
        ToCity=payload.recipient.city,
        ToState=payload.recipient.state_code,
        ToZip5=Location(payload.recipient.postal_code).as_zip5,
        ToZip4=Location(payload.recipient.postal_code).as_zip4,
        ToPhone=payload.recipient.phone_number,
        POBox=None,
        ToContactPreference=None,
        ToContactMessaging=payload.recipient.email,
        ToContactEmail=payload.recipient.email,
        AllowNonCleansedDestAddr=False,
        WeightInOunces=package.weight.OZ,
        ServiceType=service,
        Container=PackagingType[package.packaging_type or 'variable'].value,
        Width=package.width.IN,
        Length=package.length.IN,
        Height=package.height.IN,
        Girth=(package.girth.value
               if package.packaging_type == "tube" else None),
        Machinable=options["usps_option_machinable_item"],
        ProcessingCategory=None,
        PriceOptions=None,
        InsuredAmount=insurance,
        AddressServiceRequested=None,
        ExpressMailOptions=None,
        ShipDate=options.shipment_date,
        CustomerRefNo=None,
        ExtraServices=(ExtraServicesType(ExtraService=[
            getattr(option, 'value', option) for option in extra_services
        ]) if any(extra_services) else None),
        CRID=None,
        MID=None,
        LogisticsManagerMID=None,
        VendorCode=None,
        VendorProductVersionNumber=None,
        SenderName=(payload.shipper.person_name
                    or payload.shipper.company_name),
        SenderEMail=payload.shipper.email,
        RecipientName=(payload.recipient.person_name
                       or payload.recipient.company_name),
        RecipientEMail=payload.recipient.email,
        ReceiptOption="SEPARATE PAGE",
        ImageType="PDF",
        HoldForManifest=None,
        NineDigitRoutingZip=None,
        ShipInfo=options["usps_option_ship_info"],
        CarrierRelease=None,
        DropOffTime=None,
        ReturnCommitments=None,
        PrintCustomerRefNo=None,
        Content=None,
        ShippingContents=(ShippingContentsType(ItemDetail=[
            ItemDetailType(
                Description=item.description,
                Quantity=item.quantity,
                Value=item.value_amount,
                NetPounds=Weight(item.weight, WeightUnit[item.weight_unit
                                                         or 'LB']).LB,
                NetOunces=Weight(item.weight, WeightUnit[item.weight_unit
                                                         or 'LB']).OZ,
                HSTariffNumber=item.sku,
                CountryOfOrigin=Location(item.origin_country).as_country_name)
            for item in customs.commodities
        ]) if payload.customs is not None else None),
        CustomsContentType=ContentType[customs.content_type or "other"].value,
        ContentComments=None,
        RestrictionType=None,
        RestrictionComments=None,
        AESITN=customs.aes,
        ImportersReference=None,
        ImportersContact=None,
        ExportersReference=None,
        ExportersContact=None,
        InvoiceNumber=customs.invoice,
        LicenseNumber=customs.license_number,
        CertificateNumber=customs.certificate_number,
        NonDeliveryOption=non_delivery,
        AltReturnAddress1=redirect_address.address_line1,
        AltReturnAddress2=redirect_address.address_line2,
        AltReturnAddress3=None,
        AltReturnAddress4=None,
        AltReturnAddress5=None,
        AltReturnAddress6=None,
        AltReturnCountry=None,
        LabelImportType=None,
        ChargebackCode=None,
        TrackingRetentionPeriod=None,
    )

    return Serializable(request)