From 33571dfc369cc582185797534cae01ec93ea350b Mon Sep 17 00:00:00 2001 From: Daniel K Date: Tue, 17 Dec 2024 12:22:49 -0800 Subject: [PATCH 1/9] feat: add more testing data sample for webhook testing --- packages/ui/modals/webhook-test-modal.tsx | 742 ++++++++++++++-------- 1 file changed, 477 insertions(+), 265 deletions(-) diff --git a/packages/ui/modals/webhook-test-modal.tsx b/packages/ui/modals/webhook-test-modal.tsx index 41e8d9559..9322e1012 100644 --- a/packages/ui/modals/webhook-test-modal.tsx +++ b/packages/ui/modals/webhook-test-modal.tsx @@ -1,265 +1,477 @@ -import { NotificationType, WebhookType } from '@karrio/types'; -import { useWebhookMutation } from '@karrio/hooks/webhook'; -import { Notifier, Notify } from '../components/notifier'; -import { SelectField } from '../components/select-field'; -import React, { useContext, useState } from 'react'; -import json from 'highlight.js/lib/languages/json'; -import { Loading } from '../components/loader'; -import hljs from 'highlight.js/lib/core'; - -hljs.registerLanguage('json', json); - -const PLAIN_EVENT = JSON.stringify({ - event: "all", - data: { - "message": "this is a plain notification" - } -}, null, 2); - -type OperationType = { - webhook?: WebhookType; - onConfirm?: () => Promise; -}; -type WebhookTestContextType = { - testWebhook: (operation?: OperationType) => void, -}; -const WebhookTestModalContext = React.createContext({} as WebhookTestContextType); - -export const WebhookTestModal= ({ children }): JSX.Element => { - const mutation = useWebhookMutation(); - const { notify } = useContext(Notify); - const { setLoading, loading } = useContext(Loading); - const [isActive, setIsActive] = useState(false); - const [payload, setPayload] = useState(PLAIN_EVENT); - const [key, setKey] = useState(`webhook-test-${Date.now()}`); - const [operation, setOperation] = useState(); - const NOTIFICATION_SAMPLE = testPayloadSample(); - - const testWebhook = (operation?: OperationType) => { - setIsActive(true); - setOperation(operation); - setPayload(PLAIN_EVENT); - setKey(`webhook-test-${Date.now()}`); - }; - const close = (_?: React.MouseEvent) => { - setIsActive(false); - setPayload(PLAIN_EVENT); - setOperation(undefined); - setKey(`webhook-test-${Date.now()}`); - }; - const handleChange = (event: React.ChangeEvent) => { - event.preventDefault(); - setPayload(event.target.value); - }; - const handleSubmit = async (evt: React.FormEvent) => { - evt.preventDefault(); - setLoading(true); - try { - await mutation.testWebhook.mutateAsync({ - id: operation!.webhook!.id, - payload: JSON.parse(payload), - }); - notify({ type: NotificationType.success, message: `Webhook successfully Notified` }); - } catch (err: any) { - notify({ type: NotificationType.error, message: err }); - } - setLoading(false); - }; - - return ( - <> - - {children} - - - -
-
- {operation !== undefined &&
- -
- -
- Test a webhook endpoint -
-
- -

- {operation.webhook?.url} -

- - - - - {Object.entries(NOTIFICATION_SAMPLE).map(([key, value]) => ( - - ))} - - -
-
-                  
-                
-
- -
-
- - -
- -
- } - -
-
- - - ); -}; - -export function useTestWebhookModal() { - return useContext(WebhookTestModalContext); -} - - -function testPayloadSample() { - return { - "shipment purchased": JSON.stringify({ - event: "shipment.purchased", - data: { - "id": "shp_4998174814864a0690a1d0d626c101e1", - "status": "purchased", - "carrier_name": "canadapost", - "carrier_id": "canadapost", - "label": "JVBERiqrnC --- truncated base64 label ---", - "tracking_number": "123456789012", - "shipment_identifier": "123456789012", - "selected_rate": { - "id": "rat_fe4608bfc02445b387ada33e0da8d1f1", - "carrier_name": "canadapost", - "carrier_id": "canadapost", - "currency": "CAD", - "service": "canadapost_regular_parcel", - "total_charge": 46.45, - "transit_days": 10, - "extra_charges": [ - { - "name": "Fuel surcharge", - "amount": 4.17, - "currency": "CAD" - }, - { - "name": "SMB Savings", - "amount": -2.95, - "currency": "CAD" - } - ], - "meta": null, - "carrier_ref": "car_773f4a5577e4471e8918a9a1d47b208b", - "test_mode": true - }, - "selected_rate_id": "rat_fe4608bfc02445b387ada33e0da8d1f1", - "rates": [ - { - "id": "rat_fe4608bfc02445b387ada33e0da8d1f1", - "carrier_name": "canadapost", - "carrier_id": "canadapost", - "currency": "CAD", - "service": "canadapost_regular_parcel", - "total_charge": 46.45, - "transit_days": 10, - "extra_charges": [ - { - "name": "Fuel surcharge", - "amount": 4.17, - "currency": "CAD" - }, - { - "name": "SMB Savings", - "amount": -2.95, - "currency": "CAD" - } - ], - "meta": null, - "carrier_ref": "car_773f4a5577e4471e8918a9a1d47b208b", - "test_mode": true - } - ], - "tracking_url": "/v1/trackers/canadapost/123456789012?test", - "service": "canadapost_priority", - "shipper": { - "id": "adr_ab7bf955708c4d82bfe314e56a00c12a", - "postal_code": "V6M2V9", - "city": "Vancouver", - "person_name": "Jane Doe", - "company_name": "B corp.", - "country_code": "CA", - "email": null, - "phone_number": "+1 514-000-0000", - "state_code": "BC", - "residential": false, - "address_line1": "5840 Oak St", - "address_line2": null, - "validate_location": false, - }, - "recipient": { - "id": "adr_b0abf7fc21534e6280ddbd4df33b2326", - "postal_code": "E1C4Z8", - "city": "Moncton", - "person_name": "John Doe", - "company_name": "A corp.", - "country_code": "CA", - "email": null, - "phone_number": "+1 514-000-0000", - "state_code": "NB", - "residential": false, - "address_line1": "125 Church St", - "address_line2": null, - "validate_location": false, - }, - "parcels": [ - { - "id": "pcl_eecb90bb2c9c4f18acdb1b5eab8ea22a", - "weight": 1, - "width": 46, - "height": 46, - "length": 40.6, - "packaging_type": null, - "package_preset": "canadapost_corrugated_large_box", - "description": null, - "content": null, - "is_document": false, - "weight_unit": "KG", - "dimension_unit": "CM" - } - ], - "services": [], - "options": {}, - "payment": { - "paid_by": "sender", - "currency": "CAD", - "account_number": null - }, - "customs": null, - "reference": null, - "label_type": "PDF", - "carrier_ids": [], - "meta": {}, - "created_at": "2021-05-15 02:25:18.785392+00:00", - "test_mode": true, - "messages": [] - } - }, null, 2) - }; -} +import { NotificationType, WebhookType } from "@karrio/types"; +import { useWebhookMutation } from "@karrio/hooks/webhook"; +import { Notifier, Notify } from "../components/notifier"; +import { SelectField } from "../components/select-field"; +import React, { useContext, useState } from "react"; +import json from "highlight.js/lib/languages/json"; +import { Loading } from "../components/loader"; +import hljs from "highlight.js/lib/core"; + +hljs.registerLanguage("json", json); + +const PLAIN_EVENT = JSON.stringify( + { + event: "all", + data: { + message: "this is a plain notification", + }, + }, + null, + 2, +); + +type OperationType = { + webhook?: WebhookType; + onConfirm?: () => Promise; +}; +type WebhookTestContextType = { + testWebhook: (operation?: OperationType) => void; +}; +const WebhookTestModalContext = React.createContext( + {} as WebhookTestContextType, +); + +export const WebhookTestModal = ({ + children, +}: { + children: React.ReactNode; +}): JSX.Element => { + const mutation = useWebhookMutation(); + const { notify } = useContext(Notify); + const { setLoading, loading } = useContext(Loading); + const [isActive, setIsActive] = useState(false); + const [payload, setPayload] = useState(PLAIN_EVENT); + const [key, setKey] = useState(`webhook-test-${Date.now()}`); + const [operation, setOperation] = useState(); + const NOTIFICATION_SAMPLE = testPayloadSample(); + + const testWebhook = (operation?: OperationType) => { + setIsActive(true); + setOperation(operation); + setPayload(PLAIN_EVENT); + setKey(`webhook-test-${Date.now()}`); + }; + const close = (_?: React.MouseEvent) => { + setIsActive(false); + setPayload(PLAIN_EVENT); + setOperation(undefined); + setKey(`webhook-test-${Date.now()}`); + }; + const handleChange = (event: React.ChangeEvent) => { + event.preventDefault(); + setPayload(event.target.value); + }; + const handleSubmit = async (evt: React.FormEvent) => { + evt.preventDefault(); + setLoading(true); + try { + await mutation.testWebhook.mutateAsync({ + id: operation!.webhook!.id, + payload: JSON.parse(payload), + }); + notify({ + type: NotificationType.success, + message: `Webhook successfully Notified`, + }); + } catch (err: any) { + notify({ type: NotificationType.error, message: err }); + } + setLoading(false); + }; + + return ( + <> + + {children} + + + +
+
+ {operation !== undefined && ( +
+
+
+ + Test a webhook endpoint + +
+
+ +

+ {operation.webhook?.url} +

+ + + + + {Object.entries(NOTIFICATION_SAMPLE).map(([key, value]) => ( + + ))} + + +
+
+                    
+                  
+
+ +
+
+ + +
+
+ + )} + +
+
+ + ); +}; + +export function useTestWebhookModal() { + return useContext(WebhookTestModalContext); +} + +function testPayloadSample() { + return { + "shipment purchased": JSON.stringify( + { + event: "shipment.purchased", + data: { + id: "shp_4998174814864a0690a1d0d626c101e1", + status: "purchased", + carrier_name: "canadapost", + carrier_id: "canadapost", + label: "JVBERiqrnC --- truncated base64 label ---", + tracking_number: "123456789012", + shipment_identifier: "123456789012", + selected_rate: { + id: "rat_fe4608bfc02445b387ada33e0da8d1f1", + carrier_name: "canadapost", + carrier_id: "canadapost", + currency: "CAD", + service: "canadapost_regular_parcel", + total_charge: 46.45, + transit_days: 10, + extra_charges: [ + { + name: "Fuel surcharge", + amount: 4.17, + currency: "CAD", + }, + { + name: "SMB Savings", + amount: -2.95, + currency: "CAD", + }, + ], + meta: null, + carrier_ref: "car_773f4a5577e4471e8918a9a1d47b208b", + test_mode: true, + }, + selected_rate_id: "rat_fe4608bfc02445b387ada33e0da8d1f1", + rates: [ + { + id: "rat_fe4608bfc02445b387ada33e0da8d1f1", + carrier_name: "canadapost", + carrier_id: "canadapost", + currency: "CAD", + service: "canadapost_regular_parcel", + total_charge: 46.45, + transit_days: 10, + extra_charges: [ + { + name: "Fuel surcharge", + amount: 4.17, + currency: "CAD", + }, + { + name: "SMB Savings", + amount: -2.95, + currency: "CAD", + }, + ], + meta: null, + carrier_ref: "car_773f4a5577e4471e8918a9a1d47b208b", + test_mode: true, + }, + ], + tracking_url: "/v1/trackers/canadapost/123456789012?test", + service: "canadapost_priority", + shipper: { + id: "adr_ab7bf955708c4d82bfe314e56a00c12a", + postal_code: "V6M2V9", + city: "Vancouver", + person_name: "Jane Doe", + company_name: "B corp.", + country_code: "CA", + email: null, + phone_number: "+1 514-000-0000", + state_code: "BC", + residential: false, + address_line1: "5840 Oak St", + address_line2: null, + validate_location: false, + }, + recipient: { + id: "adr_b0abf7fc21534e6280ddbd4df33b2326", + postal_code: "E1C4Z8", + city: "Moncton", + person_name: "John Doe", + company_name: "A corp.", + country_code: "CA", + email: null, + phone_number: "+1 514-000-0000", + state_code: "NB", + residential: false, + address_line1: "125 Church St", + address_line2: null, + validate_location: false, + }, + parcels: [ + { + id: "pcl_eecb90bb2c9c4f18acdb1b5eab8ea22a", + weight: 1, + width: 46, + height: 46, + length: 40.6, + packaging_type: null, + package_preset: "canadapost_corrugated_large_box", + description: null, + content: null, + is_document: false, + weight_unit: "KG", + dimension_unit: "CM", + }, + ], + services: [], + options: {}, + payment: { + paid_by: "sender", + currency: "CAD", + account_number: null, + }, + customs: null, + reference: null, + label_type: "PDF", + carrier_ids: [], + meta: {}, + created_at: "2021-05-15 02:25:18.785392+00:00", + test_mode: true, + messages: [], + }, + }, + null, + 2, + ), + "tracker updated": JSON.stringify( + { + id: "evt_xxxxxxx", + type: "tracker_updated", + data: { + carrier_id: "ups", + carrier_name: "ups", + delivered: false, + events: [ + { + code: "OF", + date: "2024-12-17", + description: "Out For Delivery", + location: "Brandon, MB, CA", + time: "13:28 PM", + }, + { + code: "OF", + date: "2024-12-16", + description: + "Due to weather, your package is delayed by one business day.", + location: "Winnipeg, MB, CA", + time: "16:00 PM", + }, + { + code: "OF", + date: "2024-12-15", + description: "Arrived at Facility", + location: "Winnipeg, MB, CA", + time: "12:00 PM", + }, + { + code: "OF", + date: "2024-12-13", + description: "Departed from Facility", + location: "Concord, ON, CA", + time: "04:52 AM", + }, + { + code: "OF", + date: "2024-12-11", + description: "Arrived at Facility", + location: "Concord, ON, CA", + time: "21:37 PM", + }, + { + code: "OF", + date: "2024-12-11", + description: "Departed from Facility", + location: "Caledon, ON, CA", + time: "20:59 PM", + }, + { + code: "OF", + date: "2024-12-11", + description: "Arrived at Facility", + location: "Caledon, ON, CA", + time: "06:57 AM", + }, + { + code: "OF", + date: "2024-12-10", + description: "Departed from Facility", + location: "Lachine, QC, CA", + time: "23:16 PM", + }, + { + code: "OF", + date: "2024-12-10", + description: "Arrived at Facility", + location: "Lachine, QC, CA", + time: "13:58 PM", + }, + { + code: "OF", + date: "2024-12-09", + description: + "Shipper created a label, UPS has not received the package yet. ", + location: "CA", + time: "11:28 AM", + }, + ], + id: "trk_174406e4601f40e6abe9d68e42d877a0", + info: { + carrier_tracking_link: + "https://www.ups.com/track?loc=en_US&requester=QUIC&tracknum=1ZA82D672023568121/trackdetails", + customer_name: "GRAND SLAM PLUMBING HEATING I", + package_weight: "1.00", + package_weight_unit: "LBS", + shipment_destination_country: "CA", + shipment_destination_postal_code: "R7A6X7", + shipment_origin_country: "CA", + shipment_origin_postal_code: "R7A6X7", + shipment_package_count: "1", + shipment_service: "UPS Standard®", + source: "api", + }, + meta: {}, + metadata: {}, + object_type: "tracker", + status: "in_transit", + test_mode: false, + tracking_number: "1ZA82D672023568121", + }, + test_mode: false, + pending_webhooks: 0, + created_at: "2024-12-17T20:11:00.908316+00:00", + }, + null, + 2, + ), + "tracker created": JSON.stringify( + { + id: "evt_xxxxxxx", + type: "tracker_created", + data: { + id: "trk_4523340a1b9d48538987a7cedf162f5a", + carrier_name: "seko", + carrier_id: "seko", + tracking_number: "999880931315", + info: { + customer_name: "Zeeshan Malik", + shipment_package_count: "1", + shipment_service: "SEKO ECOMMERCE STANDARD TRACKED", + shipment_origin_country: "GB", + shipment_origin_postal_code: "TW20 8EY", + shipment_destination_country: "NL", + shipment_destination_postal_code: "2513 CA", + shipping_date: "2024-12-14", + source: "api", + }, + events: [ + { + date: "2024-12-17", + description: "International transit to destination country ", + location: "EGHAM, SURREY,GB", + code: "OP-4", + time: "11:06 AM", + }, + { + date: "2024-12-17", + description: "Processed through Export Hub", + location: "Egham, Surrey,GB", + code: "OP-3", + time: "09:18 AM", + }, + { + date: "2024-12-13", + description: + "The parcel data was entered into the GLS IT system; the parcel was not yet handed over to GLS.", + location: "GB", + code: "0.100", + time: "10:55 AM", + }, + { + date: "2024-12-13", + description: "Tracking number allocated & order ready", + location: "LONDON,EGHAM,GB", + code: "OP-1", + time: "09:55 AM", + }, + ], + delivered: false, + test_mode: false, + status: "in_transit", + meta: { carrier: "seko" }, + object_type: "tracker", + metadata: {}, + }, + }, + null, + 2, + ), + }; +} From 3996ceb930bdc73ecfc56aec40cc034773ac1f77 Mon Sep 17 00:00:00 2001 From: Daniel K Date: Tue, 17 Dec 2024 12:23:31 -0800 Subject: [PATCH 2/9] feat: add REDIS_PREFIX for redis prefix configuration --- apps/api/karrio/server/settings/cache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/api/karrio/server/settings/cache.py b/apps/api/karrio/server/settings/cache.py index 58fb46aa5..75ceaae43 100644 --- a/apps/api/karrio/server/settings/cache.py +++ b/apps/api/karrio/server/settings/cache.py @@ -9,6 +9,7 @@ REDIS_PORT = config("REDIS_PORT", default=None) REDIS_PASSWORD = config("REDIS_PASSWORD", default=None) REDIS_USERNAME = config("REDIS_USERNAME", default="default") +REDIS_PREFIX = config("REDIS_PREFIX", default="karrio") # karrio server caching setup if REDIS_HOST is not None: @@ -22,7 +23,7 @@ "BACKEND": "django_redis.cache.RedisCache", "LOCATION": REDIS_CONNECTION_URL, "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, - "KEY_PREFIX": "karrio", + "KEY_PREFIX": REDIS_PREFIX, } } print(f"Redis connection initialized at: {REDIS_CONNECTION_URL}") From bf1ff547ea7a8506364c242970d6041d2ce9b0be Mon Sep 17 00:00:00 2001 From: Daniel K Date: Tue, 17 Dec 2024 12:24:52 -0800 Subject: [PATCH 3/9] docs: REDIS_PREFIX env documentation --- apps/www/docs/product/self-hosting/environment.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/www/docs/product/self-hosting/environment.mdx b/apps/www/docs/product/self-hosting/environment.mdx index 360dc63b7..663dc7a20 100644 --- a/apps/www/docs/product/self-hosting/environment.mdx +++ b/apps/www/docs/product/self-hosting/environment.mdx @@ -84,6 +84,10 @@ The redis host name or IP address. The redis instance port. +- **`REDIS_PREFIX`** | **default:** `karrio` + +The redis key prefix. + --- ### PROVISIONING From 9cf614a98521ffd71147a19262a585271d3ec278 Mon Sep 17 00:00:00 2001 From: Daniel K Date: Wed, 18 Dec 2024 12:28:34 -0800 Subject: [PATCH 4/9] fix: easyship simple tracking support --- .../easyship/karrio/mappers/easyship/proxy.py | 8 +- .../karrio/providers/easyship/rate.py | 2 +- .../providers/easyship/shipment/create.py | 2 + .../karrio/providers/easyship/tracking.py | 47 ++++-- .../easyship/tests/easyship/test_rate.py | 153 +----------------- .../easyship/tests/easyship/test_shipment.py | 2 + .../easyship/tests/easyship/test_tracking.py | 49 +++++- 7 files changed, 97 insertions(+), 166 deletions(-) diff --git a/modules/connectors/easyship/karrio/mappers/easyship/proxy.py b/modules/connectors/easyship/karrio/mappers/easyship/proxy.py index 04edd41b7..bb5b950d1 100644 --- a/modules/connectors/easyship/karrio/mappers/easyship/proxy.py +++ b/modules/connectors/easyship/karrio/mappers/easyship/proxy.py @@ -56,10 +56,10 @@ def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]: def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]: responses = lib.run_asynchronously( - lambda easyship_shipment_id: ( - easyship_shipment_id, + lambda data: ( + data["shipment_id"], lib.request( - url=f"{self.settings.server_url}/2023-01/shipments/{easyship_shipment_id}", + url=f"{self.settings.server_url}/2023-01/shipments/{data['shipment_id']}", trace=self.trace_as("json"), method="GET", headers={ @@ -69,7 +69,7 @@ def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]: }, ), ), - request.serialize(), + [_ for _ in request.serialize() if _.get("shipment_id")], ) return lib.Deserializable( diff --git a/modules/connectors/easyship/karrio/providers/easyship/rate.py b/modules/connectors/easyship/karrio/providers/easyship/rate.py index a847d4bcf..35712663e 100644 --- a/modules/connectors/easyship/karrio/providers/easyship/rate.py +++ b/modules/connectors/easyship/karrio/providers/easyship/rate.py @@ -63,7 +63,7 @@ def _extract_details( currency=details.currency, ) for name, amount in charges - if amount is not None + if amount is not None and amount > 0 ], meta=dict( rate_provider=courier.name_or_key, diff --git a/modules/connectors/easyship/karrio/providers/easyship/shipment/create.py b/modules/connectors/easyship/karrio/providers/easyship/shipment/create.py index 38ffe0ce9..79d65d1e9 100644 --- a/modules/connectors/easyship/karrio/providers/easyship/shipment/create.py +++ b/modules/connectors/easyship/karrio/providers/easyship/shipment/create.py @@ -51,7 +51,9 @@ def _extract_details( label_type=label_type, docs=models.Documents(label=label), meta=dict( + shipment_ids=[details.easyship_shipment_id], tracking_numbers=tracking_numbers, + carrier=ctx["rate_provider"], rate_provider=ctx["rate_provider"], easyship_courier_id=ctx["courier_id"], easyship_shipment_id=details.easyship_shipment_id, diff --git a/modules/connectors/easyship/karrio/providers/easyship/tracking.py b/modules/connectors/easyship/karrio/providers/easyship/tracking.py index 90c0f1c17..e0becc43e 100644 --- a/modules/connectors/easyship/karrio/providers/easyship/tracking.py +++ b/modules/connectors/easyship/karrio/providers/easyship/tracking.py @@ -71,19 +71,40 @@ def tracking_request( payload: models.TrackingRequest, settings: provider_utils.Settings, ) -> lib.Serializable: - shipment_ids = list( - set( - [ - payload.options.get("easyship_shipment_id"), - *(payload.options.get("shipment_ids") or []), - ] - ) - ) + """Send one or multiple tracking request(s) to Easyship. + the payload must match the following schema: + { + "tracking_numbers": ["123456789"], + "shipment_ids": ["ESSG10006002"], + "options": { + "123456789": { + "carrier": "usps", + "easyship_shipment_id": "trk_xxxxxxxx", # optional + } + } + } + """ + + requests = [] - if len(shipment_ids) == 0: - raise Exception(f"easyship_shipment_id is required for tracking request") + for tracking_number in payload.tracking_numbers: + options = payload.options.get(tracking_number) or {} + shipment_id = lib.identity( + options.get("easyship_shipment_id") + or payload.options.get("easyship_shipment_id") + ) + should_add = lib.identity( + shipment_id is not None + and not any(_.get("easyship_shipment_id") == shipment_id for _ in requests) + ) - # map data to convert karrio model to easyship specific type - request = shipment_ids + if should_add: + requests.append( + dict( + tracking_number=tracking_number, + shipment_id=shipment_id, + carrier=options.get("carrier"), + ) + ) - return lib.Serializable(request, lib.to_dict) + return lib.Serializable(requests, lib.to_dict) diff --git a/modules/connectors/easyship/tests/easyship/test_rate.py b/modules/connectors/easyship/tests/easyship/test_rate.py index 0f1e8c8a8..21a982585 100644 --- a/modules/connectors/easyship/tests/easyship/test_rate.py +++ b/modules/connectors/easyship/tests/easyship/test_rate.py @@ -109,18 +109,7 @@ def test_parse_rate_response(self): "currency": "USD", "extra_charges": [ {"amount": 34.96, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, {"amount": 5.59, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, ], "meta": { "available_handover_options": ["dropoff"], @@ -144,19 +133,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 76.2, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 76.2, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff", "free_pickup"], @@ -180,19 +157,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 79.9, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 79.9, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff", "free_pickup"], @@ -216,19 +181,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 26.29, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 26.29, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff"], @@ -252,19 +205,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 48.56, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 48.56, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff", "free_pickup"], @@ -288,19 +229,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 52.26, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 52.26, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff", "free_pickup"], @@ -324,19 +253,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 91.69, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 91.69, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff"], @@ -360,19 +277,7 @@ def test_parse_rate_response(self): "carrier_name": "easyship", "currency": "USD", "extra_charges": [ - {"amount": 180.55, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, - {"amount": 0.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, + {"amount": 180.55, "currency": "USD", "name": "Shipment Charge"} ], "meta": { "available_handover_options": ["dropoff", "free_pickup"], @@ -397,18 +302,7 @@ def test_parse_rate_response(self): "currency": "USD", "extra_charges": [ {"amount": 120.64, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, {"amount": 19.0, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, ], "meta": { "available_handover_options": ["dropoff"], @@ -433,18 +327,7 @@ def test_parse_rate_response(self): "currency": "USD", "extra_charges": [ {"amount": 202.79, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, {"amount": 31.94, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, ], "meta": { "available_handover_options": ["dropoff"], @@ -469,18 +352,7 @@ def test_parse_rate_response(self): "currency": "USD", "extra_charges": [ {"amount": 210.72, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, {"amount": 33.19, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, ], "meta": { "available_handover_options": ["dropoff"], @@ -505,18 +377,7 @@ def test_parse_rate_response(self): "currency": "USD", "extra_charges": [ {"amount": 170.04, "currency": "USD", "name": "Shipment Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Insurance"}, {"amount": 26.78, "currency": "USD", "name": "Fuel Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Additional Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Duty Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Import Tax Charge"}, - {"amount": 0.0, "currency": "USD", "name": "Minimum Pickup Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Oversized Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Provincial Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Remote Area Surcharge"}, - {"amount": 0.0, "currency": "USD", "name": "Sales Tax"}, - {"amount": 0.0, "currency": "USD", "name": "Warehouse Handling Fee"}, - {"amount": 0.0, "currency": "USD", "name": "Discount"}, ], "meta": { "available_handover_options": ["dropoff"], diff --git a/modules/connectors/easyship/tests/easyship/test_shipment.py b/modules/connectors/easyship/tests/easyship/test_shipment.py index 5f8b07e1f..698e40db1 100644 --- a/modules/connectors/easyship/tests/easyship/test_shipment.py +++ b/modules/connectors/easyship/tests/easyship/test_shipment.py @@ -169,7 +169,9 @@ def test_parse_shipment_response_without_label(self): "easyship_courier_id": "b85683b8-1d32-41d7-b9af-63ae712ef3fe", "easyship_shipment_id": "ESUS220509144", "tracking_numbers": ["9405509104250026972189"], + "shipment_ids": ["ESUS220509144"], "rate_provider": "ups", + "carrier": "ups", }, "shipment_identifier": "ESUS220509144", "tracking_number": "9405509104250026972189", diff --git a/modules/connectors/easyship/tests/easyship/test_tracking.py b/modules/connectors/easyship/tests/easyship/test_tracking.py index 062f9c777..61c1f53f2 100644 --- a/modules/connectors/easyship/tests/easyship/test_tracking.py +++ b/modules/connectors/easyship/tests/easyship/test_tracking.py @@ -16,7 +16,14 @@ def setUp(self): def test_create_tracking_request(self): request = gateway.mapper.create_tracking_request(self.TrackingRequest) - self.assertEqual(request.serialize(), TrackingRequest) + self.assertListEqual(request.serialize(), TrackingRequest) + + def test_create_tracking_request_with_shipment_id(self): + request = gateway.mapper.create_tracking_request( + models.TrackingRequest(**TrackingPayload2) + ) + + self.assertListEqual(request.serialize(), TrackingRequest2) def test_get_tracking(self): with patch("karrio.mappers.easyship.proxy.lib.request") as mock: @@ -56,6 +63,26 @@ def test_parse_error_response(self): "options": {"easyship_shipment_id": "ESSG10006002"}, } +TrackingPayload2 = { + "tracking_numbers": ["89108749065090", "89108749065091"], + "options": { + "carrier": "usps", + "easyship_shipment_id": "ESSG10006002", + "tracking_numbers": ["89108749065090"], + "89108749065090": { + "carrier": "usps", + "easyship_shipment_id": "ESSG10006002", + "tracking_numbers": ["89108749065090"], + }, + "89108749065091": { + "carrier": "usps", + "easyship_shipment_id": "ESSG10006003", + "tracking_numbers": ["89108749065091"], + }, + }, +} + + ParsedTrackingResponse = [ [ { @@ -102,7 +129,25 @@ def test_parse_error_response(self): ] -TrackingRequest = ["ESSG10006002"] +TrackingRequest = [ + { + "shipment_id": "ESSG10006002", + "tracking_number": "89108749065090", + }, +] + +TrackingRequest2 = [ + { + "carrier": "usps", + "shipment_id": "ESSG10006002", + "tracking_number": "89108749065090", + }, + { + "carrier": "usps", + "shipment_id": "ESSG10006003", + "tracking_number": "89108749065091", + }, +] TrackingResponse = """{ "meta": { From 2fd78d727317dab52b3959ae81b7c9c047eae0a4 Mon Sep 17 00:00:00 2001 From: Daniel K Date: Wed, 18 Dec 2024 12:29:14 -0800 Subject: [PATCH 5/9] chore: add new carrier logos --- .../public/carriers/dhl_express_icon.svg | 9 +++- apps/dashboard/public/carriers/gls_icon.svg | 54 +++++++++++++++++++ apps/dashboard/public/carriers/seko_icon.svg | 31 +++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 apps/dashboard/public/carriers/gls_icon.svg create mode 100644 apps/dashboard/public/carriers/seko_icon.svg diff --git a/apps/dashboard/public/carriers/dhl_express_icon.svg b/apps/dashboard/public/carriers/dhl_express_icon.svg index cf3f4bf6d..8a2a79f38 100644 --- a/apps/dashboard/public/carriers/dhl_express_icon.svg +++ b/apps/dashboard/public/carriers/dhl_express_icon.svg @@ -1 +1,8 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/apps/dashboard/public/carriers/gls_icon.svg b/apps/dashboard/public/carriers/gls_icon.svg new file mode 100644 index 000000000..6352c2e20 --- /dev/null +++ b/apps/dashboard/public/carriers/gls_icon.svg @@ -0,0 +1,54 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dashboard/public/carriers/seko_icon.svg b/apps/dashboard/public/carriers/seko_icon.svg new file mode 100644 index 000000000..4c5a0676a --- /dev/null +++ b/apps/dashboard/public/carriers/seko_icon.svg @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file From 1b9e52578b438fc5b81a5da4a15c768b893fb466 Mon Sep 17 00:00:00 2001 From: Daniel K Date: Wed, 18 Dec 2024 12:30:14 -0800 Subject: [PATCH 6/9] feat: make seko event Omnicode take precedence over default Code --- modules/connectors/seko/karrio/providers/seko/tracking.py | 2 +- modules/connectors/seko/tests/seko/test_tracking.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/connectors/seko/karrio/providers/seko/tracking.py b/modules/connectors/seko/karrio/providers/seko/tracking.py index 153452dd4..e16da5731 100644 --- a/modules/connectors/seko/karrio/providers/seko/tracking.py +++ b/modules/connectors/seko/karrio/providers/seko/tracking.py @@ -54,7 +54,7 @@ def _extract_details( try_formats=["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S"], ), description=event.Description, - code=event.Code or event.OmniCode, + code=event.OmniCode or event.Code, time=lib.flocaltime( event.EventDT, try_formats=["%Y-%m-%dT%H:%M:%S.%f", "%Y-%m-%dT%H:%M:%S"], diff --git a/modules/connectors/seko/tests/seko/test_tracking.py b/modules/connectors/seko/tests/seko/test_tracking.py index e3a70614e..dd4bd5426 100644 --- a/modules/connectors/seko/tests/seko/test_tracking.py +++ b/modules/connectors/seko/tests/seko/test_tracking.py @@ -124,7 +124,7 @@ def test_parse_tracking_response_multiple(self): "delivered": False, "events": [ { - "code": "AAY", + "code": "OP-1", "date": "2024-10-25", "description": "Pre-advice received", "time": "11:28 AM", @@ -146,7 +146,7 @@ def test_parse_tracking_response_multiple(self): "delivered": False, "events": [ { - "code": "AAY", + "code": "OP-1", "date": "2024-10-25", "description": "Pre-advice received", "time": "11:56 AM", @@ -168,7 +168,7 @@ def test_parse_tracking_response_multiple(self): "delivered": False, "events": [ { - "code": "AAY", + "code": "OP-1", "date": "2024-10-25", "description": "Pre-advice received", "time": "11:57 AM", @@ -190,7 +190,7 @@ def test_parse_tracking_response_multiple(self): "delivered": False, "events": [ { - "code": "AAY", + "code": "OP-1", "date": "2024-10-25", "description": "Pre-advice received", "time": "12:02 PM", From 9c670e47cb02064bb1fb1ded1828b15c60d1f0ae Mon Sep 17 00:00:00 2001 From: Daniel K Date: Wed, 18 Dec 2024 12:31:06 -0800 Subject: [PATCH 7/9] feat: fix wording in webhooks test modal --- packages/ui/modals/webhook-test-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/modals/webhook-test-modal.tsx b/packages/ui/modals/webhook-test-modal.tsx index 9322e1012..f2f006baf 100644 --- a/packages/ui/modals/webhook-test-modal.tsx +++ b/packages/ui/modals/webhook-test-modal.tsx @@ -140,7 +140,7 @@ export const WebhookTestModal = ({ onClick={close} disabled={loading} > - Cancel + Dismiss