Exemplo n.º 1
0
    def _create_label_request(shipment_response: str) -> Job:
        activity = XP.build(document, XP.to_xml(shipment_response))
        fallback = shipment_response if activity is None else None
        data = (create_label_request(activity, payload, settings)
                if activity is None else None)

        return Job(id='create', data=data, fallback=fallback)
Exemplo n.º 2
0
def _create_pickup(availability_response: str, payload: PickupRequest,
                   settings: Settings):
    availability = XP.build(PickupAvailabilityReply,
                            XP.to_xml(availability_response))
    data = _pickup_request(payload, settings) if availability else None

    return Job(id="create_pickup", data=data, fallback="")
Exemplo n.º 3
0
def _extract_pickup_details(
    response: PickupCreationResponse, settings: Settings
) -> PickupDetails:
    pickup = XP.build(
        PickupCreationResponse,
        next(
            iter(
                response.xpath(
                    ".//*[local-name() = $name]", name="PickupCreationResponse"
                )
            ),
            None,
        ),
    )
    rate = XP.build(
        RateResultType,
        next(
            iter(response.xpath(".//*[local-name() = $name]", name="RateResult")), None
        ),
    )

    return PickupDetails(
        carrier_id=settings.carrier_id,
        carrier_name=settings.carrier_name,
        confirmation_number=pickup.PRN,
        pickup_charge=ChargeDetails(
            name=rate.RateType,
            currency=rate.CurrencyCode,
            amount=NF.decimal(rate.GrandTotalOfAllCharge),
        ),
    )
Exemplo n.º 4
0
def _extract_details(postage_node: Element, settings: Settings) -> RateDetails:
    postage: PostageType = XP.to_object(PostageType, postage_node)

    service = ServiceClassID.map(str(postage.CLASSID))
    charges: List[SpecialServiceType] = getattr(postage.SpecialServices,
                                                'SpecialService', [])
    rate = NF.decimal(XP.find('Rate', postage_node, first=True).text)
    estimated_date = DF.date(
        getattr(XP.find('CommitmentDate', postage_node, first=True), 'text',
                None))
    transit = ((estimated_date.date() - datetime.now().date()).days
               if estimated_date is not None else None)

    return RateDetails(
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,
        service=service.name_or_key,
        base_charge=rate,
        total_charge=rate,
        currency=Currency.USD.name,
        transit_days=transit,
        extra_charges=[
            ChargeDetails(
                name=charge.ServiceName,
                amount=NF.decimal(charge.Price),
                currency=Currency.USD.name,
            ) for charge in charges
        ],
        meta=dict(service_name=(service.name or postage.MailService)))
Exemplo n.º 5
0
def _extract_pickup_details(response: Element,
                            settings: Settings) -> PickupDetails:
    header = next((XP.build(PickupRequestHeaderType, elt)
                   for elt in response.xpath(".//*[local-name() = $name]",
                                             name="pickup-request-header")))
    price = next((XP.build(PickupRequestPriceType, elt)
                  for elt in response.xpath(".//*[local-name() = $name]",
                                            name="pickup-request-price")),
                 None)

    price_amount = sum(
        [
            NF.decimal(price.hst_amount or 0.0),
            NF.decimal(price.gst_amount or 0.0),
            NF.decimal(price.due_amount or 0.0),
        ],
        0.0,
    ) if price is not None else None

    return PickupDetails(
        carrier_id=settings.carrier_id,
        carrier_name=settings.carrier_name,
        confirmation_number=header.request_id,
        pickup_date=DF.fdate(header.next_pickup_date),
        pickup_charge=ChargeDetails(name="Pickup fees",
                                    amount=NF.decimal(price_amount),
                                    currency="CAD")
        if price is not None else None,
    )
Exemplo n.º 6
0
def _extract_details(shipment_node: Element,
                     settings: Settings) -> TrackingDetails:
    track_detail = XP.build(ShipmentType, shipment_node)
    activities = [
        XP.build(ActivityType, node)
        for node in shipment_node.xpath(".//*[local-name() = $name]",
                                        name="Activity")
    ]
    delivered = any(a.Status.Type == 'D' for a in activities)

    return TrackingDetails(
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,
        tracking_number=track_detail.InquiryNumber.Value,
        events=list(
            map(
                lambda a: TrackingEvent(
                    date=DF.fdate(a.Date, "%Y%m%d"),
                    description=a.Status.Description if a.Status else None,
                    location=(_format_location(a.ActivityLocation.Address)
                              if a.ActivityLocation is not None and a.
                              ActivityLocation.Address is not None else None),
                    time=DF.ftime(a.Time, "%H%M%S"),
                    code=a.Status.Code if a.Status else None,
                ),
                activities,
            )),
        delivered=delivered)
Exemplo n.º 7
0
    def extract(rates: List[RateDetails],
                detail_node: Element) -> List[RateDetails]:
        rate = XP.build(RatedShipmentType, detail_node)

        if rate.NegotiatedRateCharges is not None:
            total_charges = (rate.NegotiatedRateCharges.TotalChargesWithTaxes
                             or rate.NegotiatedRateCharges.TotalCharge)
            taxes = rate.NegotiatedRateCharges.TaxCharges
            itemized_charges = rate.NegotiatedRateCharges.ItemizedCharges + taxes
        else:
            total_charges = rate.TotalChargesWithTaxes or rate.TotalCharges
            taxes = rate.TaxCharges
            itemized_charges = rate.ItemizedCharges + taxes

        extra_charges = itemized_charges + [rate.ServiceOptionsCharges]
        estimated_arrival = next(
            (XP.build(EstimatedArrivalType, n)
             for n in detail_node.xpath(".//*[local-name() = $name]",
                                        name="EstimatedArrival")),
            EstimatedArrivalType(),
        )
        transit_days = (rate.GuaranteedDelivery.BusinessDaysInTransit
                        if rate.GuaranteedDelivery is not None else
                        estimated_arrival.BusinessDaysInTransit)
        currency_ = next(
            str(c.text)
            for c in detail_node.xpath(".//*[local-name() = $name]",
                                       name="CurrencyCode"))
        service = ShippingServiceCode(rate.Service.Code).name
        return rates + [
            RateDetails(
                carrier_name=settings.carrier_name,
                carrier_id=settings.carrier_id,
                currency=currency_,
                service=service,
                base_charge=NF.decimal(
                    rate.TransportationCharges.MonetaryValue),
                total_charge=NF.decimal(total_charges.MonetaryValue),
                duties_and_taxes=reduce(
                    lambda total, charge: total + NF.decimal(charge.
                                                             MonetaryValue),
                    taxes or [],
                    0.0,
                ),
                extra_charges=reduce(
                    lambda total, charge: (total + [
                        ChargeDetails(
                            name=charge.Code,
                            amount=NF.decimal(charge.MonetaryValue),
                            currency=charge.CurrencyCode,
                        )
                    ]),
                    [charge for charge in extra_charges if charge is not None],
                    [],
                ),
                transit_days=NF.integer(transit_days),
            )
        ]
Exemplo n.º 8
0
def parse_shipment_response(response: Element, settings: Settings) -> Tuple[ShipmentDetails, List[Message]]:
    package = XP.find("PackageID", response, ArrayOfString, first=True)
    label = XP.find("label", response, first=True)
    details = (
        _extract_details((package.string[0], str(label.text)), settings)
        if getattr(package, 'string', [None])[0] is not None else None
    )

    return details, parse_error_response(response, settings)
Exemplo n.º 9
0
    def extract(rates: List[RateDetails],
                detail_node: Element) -> List[RateDetails]:
        rate = XP.to_object(RatedShipmentType, detail_node)

        if rate.NegotiatedRateCharges is not None:
            total_charges = (rate.NegotiatedRateCharges.TotalChargesWithTaxes
                             or rate.NegotiatedRateCharges.TotalCharge)
            taxes = rate.NegotiatedRateCharges.TaxCharges
            itemized_charges = rate.NegotiatedRateCharges.ItemizedCharges + taxes
        else:
            total_charges = rate.TotalChargesWithTaxes or rate.TotalCharges
            taxes = rate.TaxCharges
            itemized_charges = rate.ItemizedCharges + taxes

        extra_charges = itemized_charges + [rate.ServiceOptionsCharges]
        estimated_arrival = (XP.find(
            "EstimatedArrival", detail_node, EstimatedArrivalType, first=True)
                             or EstimatedArrivalType())
        transit_days = (rate.GuaranteedDelivery.BusinessDaysInTransit
                        if rate.GuaranteedDelivery is not None else
                        estimated_arrival.BusinessDaysInTransit)
        currency = XP.find("CurrencyCode", detail_node, first=True).text
        service = ServiceCode.map(rate.Service.Code)

        return rates + [
            RateDetails(
                carrier_name=settings.carrier_name,
                carrier_id=settings.carrier_id,
                currency=currency,
                service=service.name_or_key,
                base_charge=NF.decimal(
                    rate.TransportationCharges.MonetaryValue),
                total_charge=NF.decimal(total_charges.MonetaryValue),
                duties_and_taxes=reduce(
                    lambda total, charge: total + NF.decimal(charge.
                                                             MonetaryValue),
                    taxes or [],
                    0.0,
                ),
                extra_charges=reduce(
                    lambda total, charge: (total + [
                        ChargeDetails(
                            name=charge.Code,
                            amount=NF.decimal(charge.MonetaryValue),
                            currency=charge.CurrencyCode,
                        )
                    ]),
                    [
                        charge for charge in extra_charges
                        if charge is not None and charge.Code is not None
                    ],
                    [],
                ),
                transit_days=NF.integer(transit_days),
                meta=dict(service_name=service.name_or_key))
        ]
Exemplo n.º 10
0
def _create_pickup(availability_response: str, payload: PickupRequest,
                   settings: Settings):
    availability = XP.build(pickup_availability,
                            XP.to_xml(availability_response))
    data = _create_pickup_request(
        payload, settings) if availability.on_demand_tour else None

    return Job(id="create_pickup",
               data=data,
               fallback="" if data is None else "")
Exemplo n.º 11
0
def parse_tracking_response(
    response: Element, settings: Settings
) -> Tuple[List[TrackingDetails], List[Message]]:
    details = XP.find("tracking-detail", response)
    tracking_details: List[TrackingDetails] = [
        _extract_tracking(node, settings) for node in details
        if len(XP.find("occurrence", node)) > 0
    ]

    return tracking_details, parse_error_response(response, settings)
Exemplo n.º 12
0
def _extract_structure_error(node: Element, settings: Settings) -> Message:
    return Message(
        # context info
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,

        # carrier error info
        code=XP.find("Code", node, first=True).text,
        message=XP.find("Message", node, first=True).text,
    )
Exemplo n.º 13
0
def _get_shipment_label(create_response: str, payload: ShipmentRequest,
                        settings: Settings) -> Job:
    response = XP.to_xml(create_response)
    valid = len(parse_error_response(response, settings)) == 0
    shipment_pin = (getattr(XP.find("ShipmentPIN", response, PIN, first=True),
                            'Value', None) if valid else None)
    data = (get_shipping_documents_request(shipment_pin, payload, settings)
            if valid else None)

    return Job(id="document", data=data, fallback="")
Exemplo n.º 14
0
def _get_pickup(update_response: str, payload: PickupUpdateRequest,
                settings: Settings) -> Job:
    errors = parse_error_response(XP.to_xml(XP.bundle_xml([update_response])),
                                  settings)
    data = None if any(
        errors
    ) else f"/enab/{settings.customer_number}/pickuprequest/{payload.confirmation_number}/details"

    return Job(id="get_pickup",
               data=Serializable(data),
               fallback="" if data is None else "")
Exemplo n.º 15
0
def _extract_shipment(response: Element, settings: Settings) -> ShipmentDetails:
    info = XP.find("shipment-info", response, ShipmentInfoType, first=True)
    label = XP.find("label", response, first=True)

    return ShipmentDetails(
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,
        tracking_number=info.tracking_pin,
        shipment_identifier=info.tracking_pin,
        label=getattr(label, "text", None),
    )
Exemplo n.º 16
0
def _extract_shipment(response: Element,
                      settings: Settings) -> ShipmentDetails:
    pin: PIN = XP.find("ShipmentPIN", response, PIN, first=True)
    document = XP.find("DocumentDetail", response, DocumentDetail,
                       first=True) or DocumentDetail()

    return ShipmentDetails(
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,
        tracking_number=pin.Value,
        shipment_identifier=pin.Value,
        label=document.Data,
    )
Exemplo n.º 17
0
def parse_error_response(response: Element,
                         settings: Settings) -> List[Message]:
    errors = XP.find("Error", response, ErrorType)
    carrier_errors = XP.find("CarrierErrorMessage", response,
                             CarrierErrorMessageType)

    return [
        *[_extract_error(er, settings) for er in errors if er.Message != ""],
        *[
            _extract_carrier_error(er, settings)
            for er in carrier_errors if er.errorMessage0 != ""
        ]
    ]
Exemplo n.º 18
0
def _get_label(shipment_response: str, settings: Settings) -> Job:
    response = XP.to_xml(shipment_response)
    shipment = XP.to_object(
        Shipment, next(iter(response.xpath(".//*[local-name() = $name]", name="shipment")), None)
    )
    success = (shipment is not None and shipment.id is not None)
    data = (
        get_label_request(LabelRequest(shipment_id=shipment.id), settings)
        if success else
        None
    )

    return Job(id="get_label", data=data, fallback=("" if not success else None))
Exemplo n.º 19
0
def parse_error_response(response, settings: Settings) -> List[Message]:
    structure_errors = XP.find("ErrorStructure", response)
    broken_rules_nodes = XP.find("brokenRules", response)
    broken_rule_nodes = XP.find("brokenRule", response)
    runtime_errors = XP.find("runtime_error", response)
    parse_errors = XP.find("parse_error", response)
    ERRORS = XP.find("ERROR", response)
    errors = XP.find("Error", response)
    faults = XP.find("fault", response)

    return [
        *[
            _extract_structure_error(node, settings)
            for node in structure_errors
        ],
        *[
            _extract_broken_rules(node, settings)
            for node in broken_rules_nodes
        ],
        *[_extract_broken_rule(node, settings) for node in broken_rule_nodes],
        *[_extract_runtime_error(node, settings) for node in runtime_errors],
        *[_extract_parse_error(node, settings) for node in parse_errors],
        *[_extract_structure_error(node, settings) for node in errors],
        *[_extract_error(node, settings) for node in ERRORS],
        *[_extract_faut(node, settings) for node in faults],
    ]
Exemplo n.º 20
0
def parse_rate_response(
        response: Element,
        settings: Settings) -> Tuple[List[RateDetails], List[Message]]:
    estimate = XP.find("GetEstimatedChargesResult",
                       response,
                       ResponseGetCharges,
                       first=True)
    product = XP.find("product", response, first=True)

    details: List[RateDetails] = [
        _extract_rate_details((product, estimate), settings)
    ]

    return details, parse_error_response(response, settings)
Exemplo n.º 21
0
    def _refund_if_submitted(shipment_details: str):
        shipment = XP.build(ShipmentInfoType, XP.to_xml(shipment_details))
        transmitted = shipment.shipment_status == 'transmitted'
        data = dict(
            id=payload.shipment_identifier,
            payload=Serializable(
                ShipmentRefundRequestType(
                    email=payload.options.get('email')), lambda request: XP.
                export(request,
                       name_='shipment-refund-request',
                       namespacedef_=
                       'xmlns="http://www.canadapost.ca/ws/shipment-v8"'))
        ) if transmitted else None

        return Job(id="refund", data=data, fallback=shipment_details)
Exemplo n.º 22
0
def _cancel_pickup_request(response: str, payload: PickupUpdateRequest,
                           settings: Settings):
    reply = next(
        iter(
            XP.to_xml(response).xpath(".//*[local-name() = $name]",
                                      name="PickupCreationResponse")),
        None,
    )
    new_pickup = XP.to_object(PickupCreationResponse, reply)
    data = (pickup_cancel_request(
        PickupCancelRequest(confirmation_number=payload.confirmation_number),
        settings,
    ) if new_pickup is not None and new_pickup.PRN is not None else None)

    return Job(id="cancel_pickup", data=data, fallback="")
Exemplo n.º 23
0
def _extract_details(node: Element, settings) -> TrackingDetails:
    info = XP.to_object(TrackInfoType, node)
    details: List[TrackDetailType] = [
        *([info.TrackSummary] or []), *info.TrackDetail
    ]
    delivered = info.StatusCategory.lower() == "delivered"

    return TrackingDetails(carrier_name=settings.carrier_name,
                           carrier_id=settings.carrier_id,
                           tracking_number=info.ID,
                           events=[
                               TrackingEvent(
                                   code=str(event.EventCode),
                                   date=DF.fdate(event.EventDate, "%B %d, %Y"),
                                   time=DF.ftime(event.EventTime, "%H:%M %p"),
                                   description=event.Event,
                                   location=", ".join([
                                       location for location in [
                                           event.EventCity,
                                           event.EventState,
                                           event.EventCountry,
                                           str(event.EventZIPCode),
                                       ] if location is not None
                                   ]),
                               ) for event in details
                           ],
                           delivered=delivered)
Exemplo n.º 24
0
def parse_address_validation_response(
        response: Element,
        settings: Settings) -> Tuple[AddressValidationDetails, List[Message]]:
    errors = parse_error_response(response, settings)
    address_node = next(
        iter(response.xpath(".//*[local-name() = $name]", name="address")),
        None)
    address = XP.to_object(CanparAddress, address_node)
    success = len(errors) == 0
    validation_details = AddressValidationDetails(
        carrier_id=settings.carrier_id,
        carrier_name=settings.carrier_name,
        success=success,
        complete_address=Address(postal_code=address.postal_code,
                                 city=address.city,
                                 company_name=address.name,
                                 country_code=address.country,
                                 email=address.email,
                                 state_code=address.province,
                                 residential=address.residential,
                                 address_line1=address.address_line_1,
                                 address_line2=SF.concat_str(
                                     address.address_line_2,
                                     address.address_line_3,
                                     join=True))) if success else None

    return validation_details, errors
Exemplo n.º 25
0
def parse_rate_response(
        response: Element,
        settings: Settings) -> Tuple[List[RateDetails], List[Message]]:
    rate_reply = XP.find("RatedShipment", response)
    rates: List[RateDetails] = reduce(_extract_package_rate(settings),
                                      rate_reply, [])
    return rates, parse_error_response(response, settings)
Exemplo n.º 26
0
def _modify_pickup(
    validation_response: str, payload: PickupUpdateRequest, settings: Settings
):
    errors = parse_error_response(XP.to_xml(validation_response), settings)
    data = _modify_pickup_request(payload, settings) if len(errors) == 0 else None

    return Job(id="modify", data=data, fallback="")
Exemplo n.º 27
0
def _extract_pickup_details(response: Element, settings: Settings) -> PickupDetails:
    pickup = XP.find(
        "PickupCreationResponse", response, PickupCreationResponse, first=True
    )
    rate = XP.find("RateResult", response, RateResultType, first=True)

    return PickupDetails(
        carrier_id=settings.carrier_id,
        carrier_name=settings.carrier_name,
        confirmation_number=pickup.PRN,
        pickup_charge=ChargeDetails(
            name=rate.RateType,
            currency=rate.CurrencyCode,
            amount=NF.decimal(rate.GrandTotalOfAllCharge),
        ),
    )
Exemplo n.º 28
0
def _extract_rate_details(node: Element, settings: Settings) -> RateDetails:
    shipment = XP.build(Shipment, node)
    surcharges = [
        ChargeDetails(name=charge.value,
                      amount=NF.decimal(getattr(shipment, charge.name)),
                      currency='CAD') for charge in list(Charges)
        if NF.decimal(getattr(shipment, charge.name)) > 0.0
    ]
    taxes = [
        ChargeDetails(name=f'{getattr(shipment, code)} Tax Charge',
                      amount=NF.decimal(charge),
                      currency='CAD')
        for code, charge in [(
            'tax_code_1',
            shipment.tax_charge_1), ('tax_code_2', shipment.tax_charge_2)]
        if NF.decimal(charge) > 0.0
    ]

    return RateDetails(
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,
        currency="CAD",
        transit_days=shipment.transit_time,
        service=Service(shipment.service_type).name,
        base_charge=NF.decimal(shipment.freight_charge),
        total_charge=sum([c.amount for c in (surcharges + taxes)], 0.0),
        duties_and_taxes=sum([t.amount for t in taxes], 0.0),
        extra_charges=(surcharges + taxes),
    )
Exemplo n.º 29
0
def _extract_details(service_node: Element, settings: Settings) -> RateDetails:
    service: ServiceType = XP.build(ServiceType, service_node)
    charges: List[ExtraServiceType] = service.ExtraServices.ExtraService
    delivery_date = DF.date(service.GuaranteeAvailability, "%m/%d/%Y")
    transit = (
        (delivery_date - datetime.now()).days
        if delivery_date is not None else None
    )

    return RateDetails(
        carrier_name=settings.carrier_name,
        carrier_id=settings.carrier_id,

        service=ServiceClassID(str(service.ID)),
        base_charge=NF.decimal(service.Postage),
        total_charge=NF.decimal(service.Postage),
        currency=Currency.USD.name,
        transit_days=transit,
        extra_charges=[
            ChargeDetails(
                name=charge.ServiceID,
                amount=NF.decimal(charge.Price),
                currency=Currency.USD.name,
            )
            for charge in charges
        ],
    )
Exemplo n.º 30
0
def _extract_tracking_details(node: Element,
                              settings: Settings) -> TrackingDetails:
    result = XP.build(TrackingResult, node)
    is_en = settings.language == "en"
    events = [
        TrackingEvent(
            date=DF.fdate(event.local_date_time, '%Y%m%d %H%M%S'),
            description=(event.code_description_en
                         if is_en else event.code_description_fr),
            location=SF.concat_str(event.address.address_line_1,
                                   event.address.address_line_2,
                                   event.address.city,
                                   event.address.province,
                                   event.address.country,
                                   join=True,
                                   separator=", "),
            code=event.code,
            time=DF.ftime(event.local_date_time, '%Y%m%d %H%M%S'),
        ) for event in cast(List[CanparTrackingEvent], result.events)
    ]

    return TrackingDetails(carrier_name=settings.carrier_name,
                           carrier_id=settings.carrier_id,
                           tracking_number=result.barcode,
                           events=events,
                           delivered=any(event.code == 'DEL'
                                         for event in events))