karrio-canadapost 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 (41) hide show
  1. karrio/mappers/canadapost/__init__.py +3 -0
  2. karrio/mappers/canadapost/mapper.py +88 -0
  3. karrio/mappers/canadapost/proxy.py +373 -0
  4. karrio/mappers/canadapost/settings.py +23 -0
  5. karrio/plugins/canadapost/__init__.py +23 -0
  6. karrio/providers/canadapost/__init__.py +25 -0
  7. karrio/providers/canadapost/error.py +42 -0
  8. karrio/providers/canadapost/manifest.py +127 -0
  9. karrio/providers/canadapost/pickup/__init__.py +3 -0
  10. karrio/providers/canadapost/pickup/cancel.py +33 -0
  11. karrio/providers/canadapost/pickup/create.py +217 -0
  12. karrio/providers/canadapost/pickup/update.py +55 -0
  13. karrio/providers/canadapost/rate.py +192 -0
  14. karrio/providers/canadapost/shipment/__init__.py +8 -0
  15. karrio/providers/canadapost/shipment/cancel.py +53 -0
  16. karrio/providers/canadapost/shipment/create.py +308 -0
  17. karrio/providers/canadapost/tracking.py +75 -0
  18. karrio/providers/canadapost/units.py +285 -0
  19. karrio/providers/canadapost/utils.py +92 -0
  20. karrio/schemas/canadapost/__init__.py +0 -0
  21. karrio/schemas/canadapost/authreturn.py +3389 -0
  22. karrio/schemas/canadapost/common.py +2037 -0
  23. karrio/schemas/canadapost/customerinfo.py +2307 -0
  24. karrio/schemas/canadapost/discovery.py +3016 -0
  25. karrio/schemas/canadapost/manifest.py +3704 -0
  26. karrio/schemas/canadapost/merchantregistration.py +1498 -0
  27. karrio/schemas/canadapost/messages.py +1431 -0
  28. karrio/schemas/canadapost/ncshipment.py +7231 -0
  29. karrio/schemas/canadapost/openreturn.py +2438 -0
  30. karrio/schemas/canadapost/pickup.py +1407 -0
  31. karrio/schemas/canadapost/pickuprequest.py +6794 -0
  32. karrio/schemas/canadapost/postoffice.py +2240 -0
  33. karrio/schemas/canadapost/rating.py +5308 -0
  34. karrio/schemas/canadapost/serviceinfo.py +1505 -0
  35. karrio/schemas/canadapost/shipment.py +9982 -0
  36. karrio/schemas/canadapost/track.py +3100 -0
  37. karrio_canadapost-2025.5rc1.dist-info/METADATA +44 -0
  38. karrio_canadapost-2025.5rc1.dist-info/RECORD +41 -0
  39. karrio_canadapost-2025.5rc1.dist-info/WHEEL +5 -0
  40. karrio_canadapost-2025.5rc1.dist-info/entry_points.txt +2 -0
  41. karrio_canadapost-2025.5rc1.dist-info/top_level.txt +3 -0
@@ -0,0 +1,308 @@
1
+ import karrio.schemas.canadapost.shipment as canadapost
2
+ import uuid
3
+ import typing
4
+ import datetime
5
+ import karrio.lib as lib
6
+ import karrio.core.units as units
7
+ import karrio.core.models as models
8
+ import karrio.providers.canadapost.error as provider_error
9
+ import karrio.providers.canadapost.units as provider_units
10
+ import karrio.providers.canadapost.utils as provider_utils
11
+
12
+
13
+ def parse_shipment_response(
14
+ _responses: lib.Deserializable[typing.Tuple[lib.Element, str]],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
17
+ responses = _responses.deserialize()
18
+
19
+ shipment_details = [
20
+ (
21
+ f"{_}",
22
+ (
23
+ _extract_shipment(response, settings, _responses.ctx)
24
+ if len(lib.find_element("shipment-id", response[0])) > 0
25
+ else None
26
+ ),
27
+ )
28
+ for _, response in enumerate(responses, start=1)
29
+ ]
30
+
31
+ shipment = lib.to_multi_piece_shipment(shipment_details)
32
+ messages: typing.List[models.Message] = sum(
33
+ [provider_error.parse_error_response(_, settings) for _, __ in responses],
34
+ start=[],
35
+ )
36
+
37
+ return shipment, messages
38
+
39
+
40
+ def _extract_shipment(
41
+ _response: typing.Tuple[lib.Element, str],
42
+ settings: provider_utils.Settings,
43
+ ctx: dict,
44
+ ) -> models.ShipmentDetails:
45
+ response, label = _response
46
+ info = lib.to_object(canadapost.ShipmentInfoType, response)
47
+
48
+ return models.ShipmentDetails(
49
+ carrier_name=settings.carrier_name,
50
+ carrier_id=settings.carrier_id,
51
+ tracking_number=info.tracking_pin,
52
+ shipment_identifier=info.shipment_id,
53
+ docs=models.Documents(label=label),
54
+ label_type=ctx["label_type"],
55
+ meta=lib.to_dict(
56
+ dict(
57
+ carrier_tracking_link=settings.tracking_url.format(info.tracking_pin),
58
+ customer_request_ids=ctx["customer_request_ids"],
59
+ manifest_required=ctx["manifest_required"],
60
+ group_id=ctx["group_id"],
61
+ )
62
+ ),
63
+ )
64
+
65
+
66
+ def shipment_request(
67
+ payload: models.ShipmentRequest,
68
+ settings: provider_utils.Settings,
69
+ ) -> lib.Serializable:
70
+ shipper = lib.to_address(payload.shipper)
71
+ recipient = lib.to_address(payload.recipient)
72
+ service = provider_units.ServiceType.map(payload.service).value_or_key
73
+ options = lib.to_shipping_options(
74
+ payload.options,
75
+ is_international=(
76
+ recipient.country_code is not None and recipient.country_code != "CA"
77
+ ),
78
+ initializer=provider_units.shipping_options_initializer,
79
+ )
80
+ packages = lib.to_packages(
81
+ payload.parcels,
82
+ provider_units.PackagePresets,
83
+ required=["weight"],
84
+ options=options,
85
+ package_option_type=provider_units.ShippingOption,
86
+ shipping_options_initializer=provider_units.shipping_options_initializer,
87
+ )
88
+
89
+ customs = lib.to_customs_info(payload.customs, weight_unit=units.WeightUnit.KG.name)
90
+ label_encoding, label_format = provider_units.LabelType.map(
91
+ payload.label_type or "PDF_4x6"
92
+ ).value
93
+ group_id = lib.fdate(datetime.datetime.now(), "%Y%m%d") + "-" + settings.carrier_id
94
+ customer_request_ids = [f"{str(uuid.uuid4().hex)}" for _ in range(len(packages))]
95
+ submit_shipment = lib.identity(
96
+ # set to true if canadapost_submit_shipment is true
97
+ options.canadapost_submit_shipment.state
98
+ # default to true if transmit_shipment_by_default is true
99
+ or (
100
+ settings.connection_config.transmit_shipment_by_default.state
101
+ and options.canadapost_submit_shipment.state is not False
102
+ )
103
+ )
104
+
105
+ requests = [
106
+ canadapost.ShipmentType(
107
+ customer_request_id=customer_request_ids[index],
108
+ groupIdOrTransmitShipment=canadapost.groupIdOrTransmitShipment(),
109
+ quickship_label_requested=None,
110
+ cpc_pickup_indicator=None,
111
+ requested_shipping_point=provider_utils.format_ca_postal_code(
112
+ shipper.postal_code
113
+ ),
114
+ shipping_point_id=None,
115
+ expected_mailing_date=options.shipment_date.state,
116
+ provide_pricing_info=True,
117
+ provide_receipt_info=None,
118
+ delivery_spec=canadapost.DeliverySpecType(
119
+ service_code=service,
120
+ sender=canadapost.SenderType(
121
+ name=shipper.person_name,
122
+ company=(shipper.company_name or "Not Applicable"),
123
+ contact_phone=(shipper.phone_number or "000 000 0000"),
124
+ address_details=canadapost.AddressDetailsType(
125
+ city=shipper.city,
126
+ prov_state=shipper.state_code,
127
+ country_code=shipper.country_code,
128
+ postal_zip_code=provider_utils.format_ca_postal_code(
129
+ shipper.postal_code
130
+ ),
131
+ address_line_1=shipper.street,
132
+ address_line_2=lib.text(shipper.address_line2),
133
+ ),
134
+ ),
135
+ destination=canadapost.DestinationType(
136
+ name=recipient.person_name,
137
+ company=recipient.company_name,
138
+ additional_address_info=None,
139
+ client_voice_number=recipient.phone_number or "000 000 0000",
140
+ address_details=canadapost.DestinationAddressDetailsType(
141
+ city=recipient.city,
142
+ prov_state=recipient.state_code,
143
+ country_code=recipient.country_code,
144
+ postal_zip_code=provider_utils.format_ca_postal_code(
145
+ recipient.postal_code
146
+ ),
147
+ address_line_1=recipient.street,
148
+ address_line_2=lib.text(recipient.address_line2),
149
+ ),
150
+ ),
151
+ parcel_characteristics=canadapost.ParcelCharacteristicsType(
152
+ weight=package.weight.map(provider_units.MeasurementOptions).KG,
153
+ dimensions=canadapost.dimensionsType(
154
+ length=package.length.map(provider_units.MeasurementOptions).CM,
155
+ width=package.width.map(provider_units.MeasurementOptions).CM,
156
+ height=package.height.map(provider_units.MeasurementOptions).CM,
157
+ ),
158
+ unpackaged=None,
159
+ mailing_tube=None,
160
+ ),
161
+ options=(
162
+ canadapost.optionsType(
163
+ option=[
164
+ canadapost.OptionType(
165
+ option_code=option.code,
166
+ option_amount=lib.to_money(option.state),
167
+ option_qualifier_1=None,
168
+ option_qualifier_2=None,
169
+ )
170
+ for _, option in package.options.items()
171
+ if option.state is not False
172
+ ]
173
+ )
174
+ if any(
175
+ [
176
+ option
177
+ for _, option in package.options.items()
178
+ if option.state is not False
179
+ ]
180
+ )
181
+ else None
182
+ ),
183
+ notification=(
184
+ canadapost.NotificationType(
185
+ email=(
186
+ package.options.email_notification_to.state
187
+ or recipient.email
188
+ ),
189
+ on_shipment=True,
190
+ on_exception=True,
191
+ on_delivery=True,
192
+ )
193
+ if package.options.email_notification.state
194
+ and any(
195
+ [package.options.email_notification_to.state, recipient.email]
196
+ )
197
+ else None
198
+ ),
199
+ print_preferences=canadapost.PrintPreferencesType(
200
+ output_format=label_format,
201
+ encoding=label_encoding,
202
+ ),
203
+ preferences=canadapost.PreferencesType(
204
+ service_code=None,
205
+ show_packing_instructions=False,
206
+ show_postage_rate=True,
207
+ show_insured_value=True,
208
+ ),
209
+ customs=(
210
+ canadapost.CustomsType(
211
+ currency=(options.currency.state or units.Currency.CAD.name),
212
+ conversion_from_cad=None,
213
+ reason_for_export="OTH",
214
+ other_reason=customs.content_type,
215
+ duties_and_taxes_prepaid=customs.duty.account_number,
216
+ certificate_number=customs.options.certificate_number.state,
217
+ licence_number=lib.text(
218
+ customs.options.license_number.state, max=10
219
+ ),
220
+ invoice_number=lib.text(customs.invoice, max=10),
221
+ sku_list=(
222
+ (
223
+ canadapost.sku_listType(
224
+ item=[
225
+ canadapost.SkuType(
226
+ customs_number_of_units=item.quantity,
227
+ customs_description=lib.text(
228
+ item.title
229
+ or item.description
230
+ or item.sku
231
+ or "N/B",
232
+ max=35,
233
+ ),
234
+ sku=item.sku or "0000",
235
+ hs_tariff_code=item.hs_code,
236
+ unit_weight=(item.weight or 1),
237
+ customs_value_per_unit=item.value_amount,
238
+ customs_unit_of_measure=None,
239
+ country_of_origin=(
240
+ item.origin_country
241
+ or shipper.country_code
242
+ ),
243
+ province_of_origin=shipper.state_code
244
+ or "N/B",
245
+ )
246
+ for item in customs.commodities
247
+ ]
248
+ )
249
+ )
250
+ if any(customs.commodities or [])
251
+ else None
252
+ ),
253
+ )
254
+ if payload.customs is not None
255
+ else None
256
+ ),
257
+ references=canadapost.ReferencesType(
258
+ cost_centre=(
259
+ options.canadapost_cost_center.state
260
+ or settings.connection_config.cost_center.state
261
+ or payload.reference
262
+ ),
263
+ customer_ref_1=payload.reference,
264
+ customer_ref_2=None,
265
+ ),
266
+ settlement_info=canadapost.SettlementInfoType(
267
+ paid_by_customer=getattr(
268
+ payload.payment, "account_number", settings.customer_number
269
+ ),
270
+ contract_id=settings.contract_id,
271
+ cif_shipment=None,
272
+ intended_method_of_payment=provider_units.PaymentType.map(
273
+ getattr(payload.payment, "paid_by", None)
274
+ ).value,
275
+ promo_code=None,
276
+ ),
277
+ ),
278
+ return_spec=None,
279
+ pre_authorized_payment=None,
280
+ create_qr_code=None,
281
+ )
282
+ for index, package in enumerate(packages)
283
+ ]
284
+
285
+ return lib.Serializable(
286
+ requests,
287
+ lambda __: [
288
+ lib.to_xml(
289
+ request,
290
+ name_="shipment",
291
+ namespacedef_='xmlns="http://www.canadapost.ca/ws/shipment-v8"',
292
+ ).replace(
293
+ "<groupIdOrTransmitShipment/>",
294
+ (
295
+ "<transmit-shipment/>"
296
+ if submit_shipment
297
+ else f"<group-id>{group_id}</group-id>"
298
+ ),
299
+ )
300
+ for request in __
301
+ ],
302
+ dict(
303
+ label_type=label_encoding,
304
+ manifest_required=(not submit_shipment),
305
+ customer_request_ids=customer_request_ids,
306
+ group_id=(None if submit_shipment else group_id),
307
+ ),
308
+ )
@@ -0,0 +1,75 @@
1
+ import karrio.schemas.canadapost.track as capost
2
+ import typing
3
+ import karrio.lib as lib
4
+ import karrio.core.models as models
5
+ import karrio.providers.canadapost.error as error
6
+ import karrio.providers.canadapost.units as provider_units
7
+ import karrio.providers.canadapost.utils as provider_utils
8
+
9
+
10
+ def parse_tracking_response(
11
+ _response: lib.Deserializable[lib.Element],
12
+ settings: provider_utils.Settings,
13
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
14
+ response = _response.deserialize()
15
+ details = lib.find_element("tracking-detail", response)
16
+ tracking_details: typing.List[models.TrackingDetails] = [
17
+ _extract_tracking(node, settings)
18
+ for node in details
19
+ if len(lib.find_element("occurrence", node)) > 0
20
+ ]
21
+
22
+ return tracking_details, error.parse_error_response(response, settings)
23
+
24
+
25
+ def _extract_tracking(
26
+ detail_node: lib.Element,
27
+ settings: provider_utils.Settings,
28
+ ) -> models.TrackingDetails:
29
+ details = lib.to_object(capost.tracking_detail, detail_node)
30
+ events: typing.List[capost.occurrenceType] = details.significant_events.occurrence
31
+ last_event = events[0]
32
+ estimated_delivery = lib.fdate(
33
+ details.changed_expected_date or details.expected_delivery_date,
34
+ "%Y-%m-%d",
35
+ )
36
+ status = next(
37
+ (
38
+ status.name
39
+ for status in list(provider_units.TrackingStatus)
40
+ if last_event.event_identifier in status.value
41
+ ),
42
+ provider_units.TrackingStatus.in_transit.name,
43
+ )
44
+
45
+ return models.TrackingDetails(
46
+ carrier_name=settings.carrier_name,
47
+ carrier_id=settings.carrier_id,
48
+ tracking_number=details.pin,
49
+ estimated_delivery=estimated_delivery,
50
+ delivered=status == "delivered",
51
+ status=status,
52
+ events=[
53
+ models.TrackingEvent(
54
+ date=lib.fdate(event.event_date, "%Y-%m-%d"),
55
+ time=lib.flocaltime(event.event_time, "%H:%M:%S"),
56
+ code=event.event_identifier,
57
+ location=lib.join(
58
+ event.event_site, event.event_province, join=True, separator=", "
59
+ ),
60
+ description=event.event_description,
61
+ )
62
+ for event in events
63
+ ],
64
+ info=models.TrackingInfo(
65
+ carrier_tracking_link=settings.tracking_url.format(details.pin),
66
+ shipment_destination_postal_code=details.destination_postal_id,
67
+ shipment_delivery_date=estimated_delivery,
68
+ shipment_service=details.service_name,
69
+ signed_by=last_event.signatory_name,
70
+ ),
71
+ )
72
+
73
+
74
+ def tracking_request(payload: models.TrackingRequest, _) -> lib.Serializable:
75
+ return lib.Serializable(payload.tracking_numbers)
@@ -0,0 +1,285 @@
1
+ import karrio.lib as lib
2
+
3
+ PRESET_DEFAULTS = dict(
4
+ dimension_unit="CM",
5
+ weight_unit="KG",
6
+ )
7
+
8
+ MeasurementOptions = lib.units.MeasurementOptionsType(
9
+ quant=0.1,
10
+ min_kg=0.01,
11
+ min_in=0.01,
12
+ )
13
+
14
+
15
+ class PackagePresets(lib.Enum):
16
+ """
17
+ Note that dimensions are in CM and weight in KG
18
+ """
19
+
20
+ canadapost_mailing_box = lib.units.PackagePreset(
21
+ **dict(width=10.2, height=15.2, length=1.0), **PRESET_DEFAULTS
22
+ )
23
+ canadapost_extra_small_mailing_box = lib.units.PackagePreset(
24
+ **dict(width=14.0, height=14.0, length=14.0), **PRESET_DEFAULTS
25
+ )
26
+ canadapost_small_mailing_box = lib.units.PackagePreset(
27
+ **dict(width=28.6, height=22.9, length=6.4), **PRESET_DEFAULTS
28
+ )
29
+ canadapost_medium_mailing_box = lib.units.PackagePreset(
30
+ **dict(width=31.0, height=23.5, length=13.3), **PRESET_DEFAULTS
31
+ )
32
+ canadapost_large_mailing_box = lib.units.PackagePreset(
33
+ **dict(width=38.1, height=30.5, length=9.5), **PRESET_DEFAULTS
34
+ )
35
+ canadapost_extra_large_mailing_box = lib.units.PackagePreset(
36
+ **dict(width=40.0, height=30.5, length=21.6), **PRESET_DEFAULTS
37
+ )
38
+ canadapost_corrugated_small_box = lib.units.PackagePreset(
39
+ **dict(width=42.0, height=32.0, length=32.0), **PRESET_DEFAULTS
40
+ )
41
+ canadapost_corrugated_medium_box = lib.units.PackagePreset(
42
+ **dict(width=46.0, height=38.0, length=32.0), **PRESET_DEFAULTS
43
+ )
44
+ canadapost_corrugated_large_box = lib.units.PackagePreset(
45
+ **dict(width=46.0, height=46.0, length=40.6), **PRESET_DEFAULTS
46
+ )
47
+ canadapost_xexpresspost_certified_envelope = lib.units.PackagePreset(
48
+ **dict(width=26.0, height=15.9, weight=0.5, length=1.5), **PRESET_DEFAULTS
49
+ )
50
+ canadapost_xexpresspost_national_large_envelope = lib.units.PackagePreset(
51
+ **dict(width=40.0, height=29.2, weight=1.36, length=1.5), **PRESET_DEFAULTS
52
+ )
53
+ canadapost_xexpresspost_regional_small_envelope = lib.units.PackagePreset(
54
+ **dict(width=26.0, height=15.9, weight=0.5, length=1.5), **PRESET_DEFAULTS
55
+ )
56
+ canadapost_xexpresspost_regional_large_envelope = lib.units.PackagePreset(
57
+ **dict(width=40.0, height=29.2, weight=1.36, length=1.5), **PRESET_DEFAULTS
58
+ )
59
+
60
+
61
+ class LabelType(lib.Enum):
62
+ PDF_4x6 = ("PDF", "4x6")
63
+ PDF_8_5x11 = ("PDF", "8.5x11")
64
+ ZPL_4x6 = ("ZPL", "4x6")
65
+
66
+ """ Unified Label type mapping """
67
+ PDF = PDF_4x6
68
+ ZPL = ZPL_4x6
69
+
70
+
71
+ class PaymentType(lib.StrEnum):
72
+ account = "Account"
73
+ card = "CreditCard"
74
+ supplier_account = "SupplierAccount"
75
+
76
+ sender = account
77
+ recipient = account
78
+ third_party = supplier_account
79
+ credit_card = card
80
+
81
+
82
+ class ConnectionConfig(lib.Enum):
83
+ cost_center = lib.OptionEnum("cost_center")
84
+ label_type = lib.OptionEnum("label_type", LabelType)
85
+ shipping_options = lib.OptionEnum("shipping_options", list)
86
+ shipping_services = lib.OptionEnum("shipping_services", list)
87
+ transmit_shipment_by_default = lib.OptionEnum("transmit_shipment_by_default", bool)
88
+
89
+
90
+ class ServiceType(lib.Enum):
91
+ canadapost_regular_parcel = "DOM.RP"
92
+ canadapost_expedited_parcel = "DOM.EP"
93
+ canadapost_xpresspost = "DOM.XP"
94
+ canadapost_xpresspost_certified = "DOM.XP.CERT"
95
+ canadapost_priority = "DOM.PC"
96
+ canadapost_library_books = "DOM.LIB"
97
+ canadapost_expedited_parcel_usa = "USA.EP"
98
+ canadapost_priority_worldwide_envelope_usa = "USA.PW.ENV"
99
+ canadapost_priority_worldwide_pak_usa = "USA.PW.PAK"
100
+ canadapost_priority_worldwide_parcel_usa = "USA.PW.PARCEL"
101
+ canadapost_small_packet_usa_air = "USA.SP.AIR"
102
+ canadapost_tracked_packet_usa = "USA.TP"
103
+ canadapost_tracked_packet_usa_lvm = "USA.TP.LVM"
104
+ canadapost_xpresspost_usa = "USA.XP"
105
+ canadapost_xpresspost_international = "INT.XP"
106
+ canadapost_international_parcel_air = "INT.IP.AIR"
107
+ canadapost_international_parcel_surface = "INT.IP.SURF"
108
+ canadapost_priority_worldwide_envelope_intl = "INT.PW.ENV"
109
+ canadapost_priority_worldwide_pak_intl = "INT.PW.PAK"
110
+ canadapost_priority_worldwide_parcel_intl = "INT.PW.PARCEL"
111
+ canadapost_small_packet_international_air = "INT.SP.AIR"
112
+ canadapost_small_packet_international_surface = "INT.SP.SURF"
113
+ canadapost_tracked_packet_international = "INT.TP"
114
+
115
+
116
+ class ShippingOption(lib.Enum):
117
+ canadapost_signature = lib.OptionEnum("SO", bool)
118
+ canadapost_coverage = lib.OptionEnum("COV", float)
119
+ canadapost_collect_on_delivery = lib.OptionEnum("COD", float)
120
+ canadapost_proof_of_age_required_18 = lib.OptionEnum("PA18", bool)
121
+ canadapost_proof_of_age_required_19 = lib.OptionEnum("PA19", bool)
122
+ canadapost_card_for_pickup = lib.OptionEnum("HFP", bool)
123
+ canadapost_do_not_safe_drop = lib.OptionEnum("DNS", bool)
124
+ canadapost_leave_at_door = lib.OptionEnum("LAD", bool)
125
+ canadapost_deliver_to_post_office = lib.OptionEnum("D2PO", bool)
126
+ canadapost_return_at_senders_expense = lib.OptionEnum("RASE", bool)
127
+ canadapost_return_to_sender = lib.OptionEnum("RTS", bool)
128
+ canadapost_abandon = lib.OptionEnum("ABAN", bool)
129
+
130
+ """ Custom Option """
131
+ canadapost_cost_center = lib.OptionEnum("cost-centre")
132
+ canadapost_submit_shipment = lib.OptionEnum("transmit-shipment", bool)
133
+
134
+ """ Unified Option type mapping """
135
+ insurance = canadapost_coverage
136
+ cash_on_delivery = canadapost_collect_on_delivery
137
+ signature_confirmation = canadapost_signature
138
+
139
+
140
+ def shipping_options_initializer(
141
+ options: dict,
142
+ package_options: lib.units.ShippingOptions = None,
143
+ is_international: bool = False,
144
+ ) -> lib.units.ShippingOptions:
145
+ _options = options.copy()
146
+
147
+ # Apply default non delivery options for if international.
148
+ no_international_option_specified: bool = not any(
149
+ key in _options for key in INTERNATIONAL_NON_DELIVERY_OPTION
150
+ )
151
+
152
+ if is_international and no_international_option_specified:
153
+ _options.update(
154
+ {ShippingOption.canadapost_return_at_senders_expense.name: True}
155
+ )
156
+
157
+ # Apply package options if specified.
158
+ if package_options is not None:
159
+ _options.update(package_options.content)
160
+
161
+ # Define carrier option filter.
162
+ def items_filter(key: str) -> bool:
163
+ return key in ShippingOption and key not in CUSTOM_OPTIONS # type:ignore
164
+
165
+ return lib.units.ShippingOptions(
166
+ _options, ShippingOption, items_filter=items_filter
167
+ )
168
+
169
+
170
+ class TrackingStatus(lib.Enum):
171
+ """Carrier tracking status mapping"""
172
+
173
+ delivered = [
174
+ "1408",
175
+ "1409",
176
+ "1421",
177
+ "1422",
178
+ "1423",
179
+ "1424",
180
+ "1425",
181
+ "1426",
182
+ "1427",
183
+ "1428",
184
+ "1429",
185
+ "1430",
186
+ "1431",
187
+ "1432",
188
+ "1433",
189
+ "1434",
190
+ "1441",
191
+ "1442",
192
+ "1496",
193
+ "1497",
194
+ "1498",
195
+ "1499",
196
+ ]
197
+ in_transit = [""]
198
+ on_hold = [
199
+ "117",
200
+ "120",
201
+ "121",
202
+ "125",
203
+ "127",
204
+ "810",
205
+ "1411",
206
+ "1414",
207
+ "1443",
208
+ "1484",
209
+ "1487",
210
+ "1494",
211
+ "2411",
212
+ "2414",
213
+ "4700",
214
+ ]
215
+ ready_for_pickup = [
216
+ "118",
217
+ "156",
218
+ "1407",
219
+ "1410",
220
+ "1435",
221
+ "1436",
222
+ "1437",
223
+ "1438",
224
+ "1479",
225
+ "1488",
226
+ "1701",
227
+ "2410",
228
+ ]
229
+ delivery_failed = [
230
+ "150",
231
+ "154",
232
+ "167",
233
+ "168",
234
+ "169",
235
+ "167",
236
+ "168",
237
+ "169",
238
+ "179",
239
+ "181",
240
+ "182",
241
+ "183",
242
+ "184",
243
+ "190",
244
+ "1100",
245
+ "1415",
246
+ "1416",
247
+ "1417",
248
+ "1418",
249
+ "1419",
250
+ "1420",
251
+ "1450",
252
+ "1481",
253
+ "1482",
254
+ "1483",
255
+ "1491",
256
+ "1492",
257
+ "1493",
258
+ "2600",
259
+ "2802",
260
+ "3001",
261
+ "4650",
262
+ ]
263
+ delivery_delayed = [
264
+ "159",
265
+ "160",
266
+ "161",
267
+ "162",
268
+ "163",
269
+ "172",
270
+ "173",
271
+ "2412",
272
+ ]
273
+ out_for_delivery = ["174", "500"]
274
+
275
+
276
+ INTERNATIONAL_NON_DELIVERY_OPTION = [
277
+ ShippingOption.canadapost_return_at_senders_expense.name,
278
+ ShippingOption.canadapost_return_to_sender.name,
279
+ ShippingOption.canadapost_abandon.name,
280
+ ]
281
+
282
+ CUSTOM_OPTIONS = [
283
+ ShippingOption.canadapost_cost_center.name,
284
+ ShippingOption.canadapost_submit_shipment.name,
285
+ ]