karrio-easyship 2025.5rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. karrio/mappers/easyship/__init__.py +3 -0
  2. karrio/mappers/easyship/mapper.py +94 -0
  3. karrio/mappers/easyship/proxy.py +155 -0
  4. karrio/mappers/easyship/settings.py +20 -0
  5. karrio/plugins/easyship/__init__.py +20 -0
  6. karrio/providers/easyship/__init__.py +25 -0
  7. karrio/providers/easyship/error.py +38 -0
  8. karrio/providers/easyship/manifest.py +72 -0
  9. karrio/providers/easyship/metadata.json +6985 -0
  10. karrio/providers/easyship/pickup/__init__.py +4 -0
  11. karrio/providers/easyship/pickup/cancel.py +42 -0
  12. karrio/providers/easyship/pickup/create.py +86 -0
  13. karrio/providers/easyship/pickup/update.py +88 -0
  14. karrio/providers/easyship/rate.py +215 -0
  15. karrio/providers/easyship/shipment/__init__.py +9 -0
  16. karrio/providers/easyship/shipment/cancel.py +39 -0
  17. karrio/providers/easyship/shipment/create.py +306 -0
  18. karrio/providers/easyship/tracking.py +110 -0
  19. karrio/providers/easyship/units.py +162 -0
  20. karrio/providers/easyship/utils.py +98 -0
  21. karrio/schemas/easyship/__init__.py +0 -0
  22. karrio/schemas/easyship/error_response.py +17 -0
  23. karrio/schemas/easyship/manifest_request.py +9 -0
  24. karrio/schemas/easyship/manifest_response.py +31 -0
  25. karrio/schemas/easyship/pickup_cancel_response.py +19 -0
  26. karrio/schemas/easyship/pickup_request.py +13 -0
  27. karrio/schemas/easyship/pickup_response.py +85 -0
  28. karrio/schemas/easyship/rate_request.py +100 -0
  29. karrio/schemas/easyship/rate_response.py +124 -0
  30. karrio/schemas/easyship/shipment_cancel_response.py +19 -0
  31. karrio/schemas/easyship/shipment_request.py +147 -0
  32. karrio/schemas/easyship/shipment_response.py +273 -0
  33. karrio/schemas/easyship/tracking_request.py +49 -0
  34. karrio/schemas/easyship/tracking_response.py +54 -0
  35. karrio_easyship-2025.5rc1.dist-info/METADATA +45 -0
  36. karrio_easyship-2025.5rc1.dist-info/RECORD +39 -0
  37. karrio_easyship-2025.5rc1.dist-info/WHEEL +5 -0
  38. karrio_easyship-2025.5rc1.dist-info/entry_points.txt +2 -0
  39. karrio_easyship-2025.5rc1.dist-info/top_level.txt +3 -0
@@ -0,0 +1,4 @@
1
+
2
+ from karrio.providers.easyship.pickup.create import parse_pickup_response, pickup_request
3
+ from karrio.providers.easyship.pickup.update import parse_pickup_update_response, pickup_update_request
4
+ from karrio.providers.easyship.pickup.cancel import parse_pickup_cancel_response, pickup_cancel_request
@@ -0,0 +1,42 @@
1
+ import typing
2
+ import karrio.lib as lib
3
+ import karrio.core.units as units
4
+ import karrio.core.models as models
5
+ import karrio.providers.easyship.error as error
6
+ import karrio.providers.easyship.utils as provider_utils
7
+ import karrio.providers.easyship.units as provider_units
8
+
9
+
10
+ def parse_pickup_cancel_response(
11
+ _response: lib.Deserializable[dict],
12
+ settings: provider_utils.Settings,
13
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
14
+ response = _response.deserialize()
15
+ messages = error.parse_error_response(response, settings)
16
+ success = response.get("success") is not None
17
+
18
+ confirmation = (
19
+ models.ConfirmationDetails(
20
+ carrier_id=settings.carrier_id,
21
+ carrier_name=settings.carrier_name,
22
+ operation="Cancel Pickup",
23
+ success=success,
24
+ )
25
+ if success
26
+ else None
27
+ )
28
+
29
+ return confirmation, messages
30
+
31
+
32
+ def pickup_cancel_request(
33
+ payload: models.PickupCancelRequest,
34
+ settings: provider_utils.Settings,
35
+ ) -> lib.Serializable:
36
+
37
+ # map data to convert karrio model to easyship specific type
38
+ request = dict(
39
+ easyship_pickup_id=payload.confirmation_number,
40
+ )
41
+
42
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,86 @@
1
+ """Karrio Easyship pickup API implementation."""
2
+
3
+ import karrio.schemas.easyship.pickup_request as easyship
4
+ import karrio.schemas.easyship.pickup_response as pickup
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.units as units
9
+ import karrio.core.models as models
10
+ import karrio.providers.easyship.error as error
11
+ import karrio.providers.easyship.utils as provider_utils
12
+ import karrio.providers.easyship.units as provider_units
13
+
14
+
15
+ def parse_pickup_response(
16
+ _response: lib.Deserializable[dict],
17
+ settings: provider_utils.Settings,
18
+ ) -> typing.Tuple[typing.List[models.PickupDetails], typing.List[models.Message]]:
19
+ response = _response.deserialize()
20
+
21
+ messages = error.parse_error_response(response, settings)
22
+ pickup = lib.identity(
23
+ _extract_details(response, settings)
24
+ if response.get("pickup") is not None
25
+ else None
26
+ )
27
+
28
+ return pickup, messages
29
+
30
+
31
+ def _extract_details(
32
+ data: dict,
33
+ settings: provider_utils.Settings,
34
+ ) -> models.PickupDetails:
35
+ details = lib.to_object(pickup.PickupResponseType, data)
36
+
37
+ return models.PickupDetails(
38
+ carrier_id=settings.carrier_id,
39
+ carrier_name=settings.carrier_name,
40
+ confirmation_number=details.pickup.easyship_pickup_id,
41
+ pickup_date=lib.fdate(details.pickup.selected_from_time, "%Y-%m-%dT%H:%M"),
42
+ ready_time=lib.ftime(details.pickup.selected_from_time, "%Y-%m-%dT%H:%M"),
43
+ closing_time=lib.ftime(details.pickup.selected_to_time, "%Y-%m-%dT%H:%M"),
44
+ meta=dict(
45
+ easyship_courier_id=details.pickup.courier.id,
46
+ easyship_pickup_id=details.pickup.easyship_pickup_id,
47
+ easyship_shipment_ids=details.meta.easyship_shipment_ids,
48
+ ),
49
+ )
50
+
51
+
52
+ def pickup_request(
53
+ payload: models.PickupRequest,
54
+ settings: provider_utils.Settings,
55
+ ) -> lib.Serializable:
56
+ options = lib.units.Options(
57
+ payload.options,
58
+ option_type=lib.units.create_enum(
59
+ "PickupOptions",
60
+ {
61
+ "shipments": lib.OptionEnum("shipments", list),
62
+ "easyship_time_slot_id": lib.OptionEnum("time_slot_id", str),
63
+ "shipment_identifiers": lib.OptionEnum("shipment_identifiers", list),
64
+ "easyship_courier_account_id": lib.OptionEnum(
65
+ "courier_account_id", str
66
+ ),
67
+ },
68
+ ),
69
+ )
70
+ easyship_shipment_ids = lib.identity(
71
+ [_["shipment_identifier"] for _ in options.shipments.state]
72
+ if any(options.shipments.state or [])
73
+ else options.shipment_identifiers.state
74
+ )
75
+
76
+ # map data to convert karrio model to easyship specific type
77
+ request = easyship.PickupRequestType(
78
+ easyship_shipment_ids=easyship_shipment_ids,
79
+ time_slot_id=options.easyship_time_slot_id.state,
80
+ courier_id=options.easyship_courier_account_id.state,
81
+ selected_from_time=lib.ftime(payload.ready_time, "%H:%M"),
82
+ selected_to_time=lib.ftime(payload.closing_time, "%H:%M"),
83
+ selected_date=lib.fdate(payload.pickup_date),
84
+ )
85
+
86
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,88 @@
1
+ """Karrio Easyship pickup updateAPI implementation."""
2
+
3
+ import karrio.schemas.easyship.pickup_request as easyship
4
+ import karrio.schemas.easyship.pickup_response as pickup
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.units as units
9
+ import karrio.core.models as models
10
+ import karrio.providers.easyship.error as error
11
+ import karrio.providers.easyship.utils as provider_utils
12
+ import karrio.providers.easyship.units as provider_units
13
+
14
+
15
+ def parse_pickup_update_response(
16
+ _response: lib.Deserializable[dict],
17
+ settings: provider_utils.Settings,
18
+ ) -> typing.Tuple[typing.List[models.PickupDetails], typing.List[models.Message]]:
19
+ response = _response.deserialize()
20
+
21
+ messages = error.parse_error_response(response, settings)
22
+ pickup = lib.identity(
23
+ _extract_details(response, settings)
24
+ if response.get("pickup") is not None
25
+ else None
26
+ )
27
+
28
+ return pickup, messages
29
+
30
+
31
+ def _extract_details(
32
+ data: dict,
33
+ settings: provider_utils.Settings,
34
+ ) -> models.PickupDetails:
35
+ details = lib.to_object(pickup.PickupResponseType, data)
36
+
37
+ return models.PickupDetails(
38
+ carrier_id=settings.carrier_id,
39
+ carrier_name=settings.carrier_name,
40
+ confirmation_number=details.pickup.easyship_pickup_id,
41
+ pickup_date=lib.fdate(details.pickup.selected_from_time, "%Y-%m-%dT%H:%M"),
42
+ ready_time=lib.ftime(details.pickup.selected_from_time, "%Y-%m-%dT%H:%M"),
43
+ closing_time=lib.ftime(details.pickup.selected_to_time, "%Y-%m-%dT%H:%M"),
44
+ meta=dict(
45
+ easyship_courier_id=details.pickup.courier.id,
46
+ easyship_pickup_id=details.pickup.easyship_pickup_id,
47
+ easyship_shipment_ids=details.meta.easyship_shipment_ids,
48
+ ),
49
+ )
50
+
51
+
52
+ def pickup_update_request(
53
+ payload: models.PickupUpdateRequest,
54
+ settings: provider_utils.Settings,
55
+ ) -> lib.Serializable:
56
+ options = lib.units.Options(
57
+ payload.options,
58
+ option_type=lib.units.create_enum(
59
+ "PickupOptions",
60
+ {
61
+ "shipments": lib.OptionEnum("shipments", list),
62
+ "easyship_time_slot_id": lib.OptionEnum("time_slot_id", str),
63
+ "shipment_identifiers": lib.OptionEnum("shipment_identifiers", list),
64
+ "easyship_courier_account_id": lib.OptionEnum(
65
+ "courier_account_id", str
66
+ ),
67
+ },
68
+ ),
69
+ )
70
+ easyship_shipment_ids = lib.identity(
71
+ [_["shipment_identifier"] for _ in options.shipments.state]
72
+ if any(options.shipments.state or [])
73
+ else options.shipment_identifiers.state
74
+ )
75
+
76
+ # map data to convert karrio model to easyship specific type
77
+ request = easyship.PickupRequestType(
78
+ easyship_shipment_ids=easyship_shipment_ids,
79
+ time_slot_id=options.easyship_time_slot_id.state,
80
+ courier_id=options.easyship_courier_account_id.state,
81
+ selected_from_time=lib.ftime(payload.ready_time, "%H:%M"),
82
+ selected_to_time=lib.ftime(payload.closing_time, "%H:%M"),
83
+ selected_date=lib.fdate(payload.pickup_date),
84
+ )
85
+
86
+ return lib.Serializable(
87
+ request, lib.to_dict, dict(easyship_pickup_id=payload.confirmation_number)
88
+ )
@@ -0,0 +1,215 @@
1
+ """Karrio Easyship rating API implementation."""
2
+
3
+ import karrio.schemas.easyship.rate_request as easyship
4
+ import karrio.schemas.easyship.rate_response as rating
5
+
6
+ import typing
7
+ import karrio.lib as lib
8
+ import karrio.core.units as units
9
+ import karrio.core.models as models
10
+ import karrio.providers.easyship.error as error
11
+ import karrio.providers.easyship.utils as provider_utils
12
+ import karrio.providers.easyship.units as provider_units
13
+
14
+
15
+ def parse_rate_response(
16
+ _response: lib.Deserializable[dict],
17
+ settings: provider_utils.Settings,
18
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
19
+ response = _response.deserialize()
20
+
21
+ messages = error.parse_error_response(response, settings)
22
+ rates = [_extract_details(rate, settings) for rate in response.get("rates", [])]
23
+
24
+ return rates, messages
25
+
26
+
27
+ def _extract_details(
28
+ data: dict,
29
+ settings: provider_utils.Settings,
30
+ ) -> models.RateDetails:
31
+ details = lib.to_object(rating.RateType, data)
32
+ service = provider_units.ShippingServiceID.map(details.courier_id)
33
+ courier = provider_units.ShippingCourierID.find(service.value_or_key)
34
+
35
+ charges = [
36
+ ("Shipment Charge", details.shipment_charge),
37
+ ("Insurance", details.insurance_fee),
38
+ ("Other Surcharges", getattr(details.other_surcharges, "total_fee", 0.0)),
39
+ ("Fuel Surcharge", details.fuel_surcharge),
40
+ ("Additional Surcharge", details.additional_services_surcharge),
41
+ ("Import Duty Charge", details.import_duty_charge),
42
+ ("Import Tax Charge", details.import_tax_charge),
43
+ ("Minimum Pickup Fee", details.minimum_pickup_fee),
44
+ ("Oversized Surcharge", details.oversized_surcharge),
45
+ ("Provincial Sales Tax", details.provincial_sales_tax),
46
+ ("Remote Area Surcharge", details.remote_area_surcharge),
47
+ ("Sales Tax", details.sales_tax),
48
+ ("Warehouse Handling Fee", details.warehouse_handling_fee),
49
+ ("Discount", lib.failsafe(lambda: details.discount.amount)),
50
+ ]
51
+
52
+ return models.RateDetails(
53
+ carrier_id=settings.carrier_id,
54
+ carrier_name=settings.carrier_name,
55
+ service=service.value_or_key,
56
+ total_charge=lib.to_money(details.total_charge),
57
+ currency=details.currency,
58
+ transit_days=details.max_delivery_time,
59
+ extra_charges=[
60
+ models.ChargeDetails(
61
+ name=name,
62
+ amount=lib.to_money(amount),
63
+ currency=details.currency,
64
+ )
65
+ for name, amount in charges
66
+ if amount is not None and amount > 0
67
+ ],
68
+ meta=dict(
69
+ rate_provider=courier.name_or_key,
70
+ easyship_incoterms=details.incoterms,
71
+ easyship_courier_id=details.courier_id,
72
+ service_name=service.value or details.courier_name,
73
+ available_handover_options=details.available_handover_options,
74
+ value_for_money_rank=details.value_for_money_rank,
75
+ tracking_rating=details.tracking_rating,
76
+ min_delivery_time=details.min_delivery_time,
77
+ max_delivery_time=details.max_delivery_time,
78
+ delivery_time_rank=details.delivery_time_rank,
79
+ cost_rank=details.cost_rank,
80
+ ),
81
+ )
82
+
83
+
84
+ def rate_request(
85
+ payload: models.RateRequest,
86
+ settings: provider_utils.Settings,
87
+ ) -> lib.Serializable:
88
+ shipper = lib.to_address(payload.shipper)
89
+ recipient = lib.to_address(payload.recipient)
90
+ packages = lib.to_packages(payload.parcels)
91
+ weight_unit, dimension_unit = packages.compatible_units
92
+ options = lib.to_shipping_options(
93
+ payload.options,
94
+ package_options=packages.options,
95
+ initializer=provider_units.shipping_options_initializer,
96
+ )
97
+ incoterms = lib.identity(
98
+ options.easyship_incoterms.state
99
+ or getattr(getattr(payload, "customs", None), "incoterm", None)
100
+ )
101
+
102
+ # map data to convert karrio model to easyship specific type
103
+ request = easyship.RateRequestType(
104
+ courier_selection=lib.identity(
105
+ easyship.CourierSelectionType(
106
+ apply_shipping_rules=lib.identity(
107
+ options.easyship_apply_shipping_rules.state
108
+ if options.easyship_apply_shipping_rules.state is not None
109
+ else settings.connection_config.apply_shipping_rules.state
110
+ ),
111
+ show_courier_logo_url=options.easyship_show_courier_logo_url.state,
112
+ )
113
+ if any(
114
+ [
115
+ settings.connection_config.apply_shipping_rules.state,
116
+ options.easyship_apply_shipping_rules.state,
117
+ options.easyship_show_courier_logo_url.state,
118
+ ]
119
+ )
120
+ else None
121
+ ),
122
+ destination_address=easyship.NAddressType(
123
+ country_alpha2=recipient.country_code,
124
+ city=recipient.city,
125
+ company_name=lib.text(recipient.company_name or "N/A", max=22),
126
+ contact_email=recipient.email,
127
+ contact_name=lib.text(recipient.person_name, max=22),
128
+ contact_phone=recipient.phone_number,
129
+ line_1=recipient.address_line1,
130
+ line_2=recipient.address_line2,
131
+ postal_code=recipient.postal_code,
132
+ state=recipient.state_code,
133
+ ),
134
+ incoterms=incoterms,
135
+ insurance=easyship.InsuranceType(
136
+ insured_amount=options.insurance.state,
137
+ insured_currency=lib.identity(
138
+ options.currency.state if options.insurance.state is not None else None
139
+ ),
140
+ is_insured=options.insurance.state is not None,
141
+ ),
142
+ origin_address=easyship.NAddressType(
143
+ country_alpha2=shipper.country_code,
144
+ city=shipper.city,
145
+ company_name=lib.text(shipper.company_name or "N/A", max=22),
146
+ contact_email=shipper.email,
147
+ contact_name=lib.text(shipper.person_name, max=22),
148
+ contact_phone=shipper.phone_number,
149
+ line_1=shipper.address_line1,
150
+ line_2=shipper.address_line2,
151
+ postal_code=shipper.postal_code,
152
+ state=shipper.state_code,
153
+ ),
154
+ parcels=[
155
+ easyship.ParcelType(
156
+ box=easyship.BoxType(
157
+ height=package.height.value,
158
+ length=package.length.value,
159
+ width=package.width.value,
160
+ slug=package.options.easyship_box_slug.state,
161
+ ),
162
+ items=[
163
+ easyship.ItemType(
164
+ dimensions=None,
165
+ declared_currency=lib.identity(
166
+ item.value_currency or options.currency.state or "USD"
167
+ ),
168
+ origin_country_alpha2=lib.identity(
169
+ item.origin_country or shipper.country_code
170
+ ),
171
+ quantity=item.quantity,
172
+ actual_weight=item.weight,
173
+ category=item.category or "bags_luggages",
174
+ declared_customs_value=item.value_amount,
175
+ description=item.description or item.title or "Item",
176
+ sku=item.sku or "N/A",
177
+ hs_code=item.hs_code or "N/A",
178
+ contains_liquids=item.metadata.get("contains_liquids"),
179
+ contains_battery_pi966=item.metadata.get(
180
+ "contains_battery_pi966"
181
+ ),
182
+ contains_battery_pi967=item.metadata.get(
183
+ "contains_battery_pi967"
184
+ ),
185
+ )
186
+ for item in lib.identity(
187
+ package.items
188
+ if any(package.items)
189
+ else [
190
+ models.Commodity(
191
+ title=lib.text(package.description, max=35),
192
+ description=package.description,
193
+ quantity=1,
194
+ hs_code="N/A",
195
+ value_amount=1.0,
196
+ value_currency=options.currency.state or "USD",
197
+ category="bags_luggages",
198
+ )
199
+ ]
200
+ )
201
+ ],
202
+ total_actual_weight=package.weight.value,
203
+ )
204
+ for package in packages
205
+ ],
206
+ shipping_settings=easyship.ShippingSettingsType(
207
+ output_currency=options.currency.state,
208
+ units=easyship.UnitsType(
209
+ dimensions=provider_units.DimensionUnit.map(dimension_unit.name).value,
210
+ weight=provider_units.WeightUnit.map(weight_unit.name).value,
211
+ ),
212
+ ),
213
+ )
214
+
215
+ return lib.Serializable(request, lib.to_dict)
@@ -0,0 +1,9 @@
1
+
2
+ from karrio.providers.easyship.shipment.create import (
3
+ parse_shipment_response,
4
+ shipment_request,
5
+ )
6
+ from karrio.providers.easyship.shipment.cancel import (
7
+ parse_shipment_cancel_response,
8
+ shipment_cancel_request,
9
+ )
@@ -0,0 +1,39 @@
1
+ import typing
2
+ import karrio.lib as lib
3
+ import karrio.core.models as models
4
+ import karrio.providers.easyship.error as error
5
+ import karrio.providers.easyship.utils as provider_utils
6
+ import karrio.providers.easyship.units as provider_units
7
+
8
+
9
+ def parse_shipment_cancel_response(
10
+ _response: lib.Deserializable[dict],
11
+ settings: provider_utils.Settings,
12
+ ) -> typing.Tuple[models.ConfirmationDetails, typing.List[models.Message]]:
13
+ response = _response.deserialize()
14
+ messages = error.parse_error_response(response, settings)
15
+ success = response.get("success") is not None
16
+
17
+ confirmation = (
18
+ models.ConfirmationDetails(
19
+ carrier_id=settings.carrier_id,
20
+ carrier_name=settings.carrier_name,
21
+ operation="Cancel Shipment",
22
+ success=success,
23
+ )
24
+ if success
25
+ else None
26
+ )
27
+
28
+ return confirmation, messages
29
+
30
+
31
+ def shipment_cancel_request(
32
+ payload: models.ShipmentCancelRequest,
33
+ settings: provider_utils.Settings,
34
+ ) -> lib.Serializable:
35
+
36
+ # map data to convert karrio model to easyship specific type
37
+ request = dict(easyship_shipment_id=payload.shipment_identifier)
38
+
39
+ return lib.Serializable(request, lib.to_dict)