Skip to content

Commit

Permalink
feat: initial SEKO integration with unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
danh91 committed Sep 4, 2024
1 parent 4c04aef commit aa3c8d9
Show file tree
Hide file tree
Showing 16 changed files with 605 additions and 321 deletions.
7 changes: 5 additions & 2 deletions modules/connectors/seko/karrio/mappers/seko/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
},
)

return lib.Deserializable(response, lib.to_dict)
return lib.Deserializable(response, lib.to_dict, request.ctx)

def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
response = lib.request(
Expand All @@ -48,7 +48,10 @@ def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
},
)

return lib.Deserializable(response, lib.to_dict)
return lib.Deserializable(
response,
lib.to_dict,
)

def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
response = lib.request(
Expand Down
47 changes: 43 additions & 4 deletions modules/connectors/seko/karrio/providers/seko/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,54 @@ def parse_error_response(
**kwargs,
) -> typing.List[models.Message]:
responses = response if isinstance(response, list) else [response]
errors: list = [] # compute the carrier error object list
errors: list = sum(
[
[
*lib.identity(
[
{"code": "Error", "message": e.get("Message"), **e}
for e in _.get("Errors", [])
]
if any(_.get("Errors", []))
else []
),
*lib.identity(
[
{"code": "Error", "message": e.get("Message"), **e}
for e in _.get("Error", [])
]
if any(_.get("Error", []))
else []
),
*lib.identity(
[
{"code": "Rejected", "message": e.get("Reason"), **e}
for e in _.get("Error", [])
]
if any(_.get("Rejected", []))
else []
),
*lib.identity(
[
{"code": "ValidationError", "message": e.get("Message"), **e}
for e in [_.get("ValidationErrors")]
]
if _.get("ValidationErrors")
else []
),
]
for _ in responses
],
[],
)

return [
models.Message(
carrier_id=settings.carrier_id,
carrier_name=settings.carrier_name,
code="",
message="",
details={**kwargs},
code=error["code"],
message=error["message"],
details={**kwargs, **error},
)
for error in errors
]
25 changes: 19 additions & 6 deletions modules/connectors/seko/karrio/providers/seko/manifest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Karrio SEKO Logistics manifest API implementation."""

import karrio.schemas.seko.manifest_request as seko
import karrio.schemas.seko.manifest_response as manifest

import typing
Expand All @@ -18,7 +17,11 @@ def parse_manifest_response(
response = _response.deserialize()

messages = error.parse_error_response(response, settings)
details = _extract_details(response, settings)
details = lib.identity(
_extract_details(response, settings)
if any(response.get("OutboundManifest") or [])
else None
)

return details, messages

Expand All @@ -27,14 +30,24 @@ def _extract_details(
data: dict,
settings: provider_utils.Settings,
) -> models.ManifestDetails:
details = None # manifest details parsing
manifest = None # extract carrier manifest file
details = lib.to_object(manifest.ManifestResponseType, data)
manifest_numbers = [_.ManifestNumber for _ in details.OutboundManifest]
manifest_connotes = sum(
[_.ManifestedConnotes for _ in details.OutboundManifest], []
)
manifest_doc = lib.bundle_base64(
[_.ManifestContent for _ in details.OutboundManifest], "PDF"
)

return models.ManifestDetails(
carrier_id=settings.carrier_id,
carrier_name=settings.carrier_id,
doc=models.ManifestDocument(manifest=manifest),
meta=dict(),
doc=models.ManifestDocument(manifest=manifest_doc),
meta=dict(
ManifestNumber=manifest_numbers[0],
ManifestNumbers=manifest_numbers,
ManifestConnotes=manifest_connotes,
),
)


Expand Down
81 changes: 46 additions & 35 deletions modules/connectors/seko/karrio/providers/seko/rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def parse_rate_response(
response = _response.deserialize()

messages = error.parse_error_response(response, settings)
rates = [_extract_details(rate, settings) for rate in response]
rates = [_extract_details(rate, settings) for rate in response.get("Available", [])]

return rates, messages

Expand All @@ -28,17 +28,25 @@ def _extract_details(
data: dict,
settings: provider_utils.Settings,
) -> models.RateDetails:
details = None # parse carrier rate type
details = lib.to_object(rating.AvailableType, data)
service = provider_units.ShippingService.map(details.CarrierServiceType)

return models.RateDetails(
carrier_id=settings.carrier_id,
carrier_name=settings.carrier_name,
service="", # extract service from rate
total_charge=lib.to_money(0.0), # extract the rate total rate cost
currency="", # extract the rate pricing currency
transit_days=0, # extract the rate transit days
service=service.name_or_key,
total_charge=lib.to_money(details.Cost),
currency="USD",
meta=dict(
service_name="", # extract the rate service human readable name
service_name=service.value_or_key,
seko_carrier=details.CarrierName,
Route=details.Route,
QuoteId=details.QuoteId,
DeliveryType=details.DeliveryType,
CarrierServiceType=details.CarrierServiceType,
IsFreightForward=details.IsFreightForward,
IsRuralDelivery=details.IsRuralDelivery,
IsSaturdayDelivery=details.IsSaturdayDelivery,
),
)

Expand All @@ -47,49 +55,52 @@ def rate_request(
payload: models.RateRequest,
settings: provider_utils.Settings,
) -> lib.Serializable:
shipper = lib.to_address(payload.shipper)
recipient = lib.to_address(payload.recipient)
packages = lib.to_packages(payload.parcels)
services = lib.to_services(payload.services, provider_units.ShippingService)
options = lib.to_shipping_options(
payload.options,
package_options=packages.options,
initializer=provider_units.shipping_options_initializer,
)
packages = lib.to_packages(
payload.parcels,
options=options,
shipping_options_initializer=provider_units.shipping_options_initializer,
)

# map data to convert karrio model to seko specific type
request = seko.RateRequestType(
DeliveryReference=None,
request = seko.RatingRequestType(
DeliveryReference=payload.reference,
Destination=seko.DestinationType(
Id=None,
Name=None,
Id=options.seko_destination_id.state,
Name=recipient.company_name,
Address=seko.AddressType(
BuildingName=None,
StreetAddress=None,
BuildingName="",
StreetAddress=recipient.street,
Suburb=None,
City=None,
PostCode=None,
CountryCode=None,
City=recipient.city,
PostCode=recipient.postal_code,
CountryCode=recipient.country_code,
),
ContactPerson=None,
PhoneNumber=None,
Email=None,
DeliveryInstructions=None,
RecipientTaxId=None,
ContactPerson=recipient.contact,
PhoneNumber=recipient.phone_number,
Email=recipient.email,
DeliveryInstructions=options.destination_instructions.state,
RecipientTaxId=recipient.tax_id,
),
IsSaturdayDelivery=None,
IsSignatureRequired=None,
IsSaturdayDelivery=options.seko_is_saturday_delivery.state,
IsSignatureRequired=options.seko_is_signature_required.state,
Packages=[
seko.PackageType(
Height=None,
Length=None,
Id=None,
Width=None,
Kg=None,
Name=None,
PackageCode=None,
Type=None,
Height=package.height.CM,
Length=package.length.CM,
Id=package.options.seko_package_id.state,
Width=package.width.CM,
Kg=package.weight.KG,
Name=lib.text(package.description, max=50),
Type=provider_units.PackagingType.map(
package.packaging_type or "your_packaging"
).value,
)
for package in packages
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ def shipment_cancel_request(
"PickupOptions",
# fmt: off
{
"shipment_ids": lib.OptionEnum("shipment_ids", lib.to_list),
"shipment_identifiers": lib.OptionEnum("shipment_identifiers", lib.to_list),
},
# fmt: on
),
)

# map data to convert karrio model to seko specific type
request = lib.identity(options.shipment_ids.state or [payload.shipment_identifier])
request = lib.identity(
options.shipment_identifiers.state or [payload.shipment_identifier]
)

return lib.Serializable(request, lib.to_dict)
Loading

0 comments on commit aa3c8d9

Please sign in to comment.