karrio-colissimo 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.
@@ -0,0 +1,3 @@
1
+ from karrio.mappers.colissimo.mapper import Mapper
2
+ from karrio.mappers.colissimo.proxy import Proxy
3
+ from karrio.mappers.colissimo.settings import Settings
@@ -0,0 +1,41 @@
1
+ """Karrio Colissimo client mapper."""
2
+
3
+ import typing
4
+ import karrio.lib as lib
5
+ import karrio.api.mapper as mapper
6
+ import karrio.core.models as models
7
+ import karrio.providers.colissimo as provider
8
+ import karrio.mappers.colissimo.settings as provider_settings
9
+ import karrio.universal.providers.rating as universal_provider
10
+
11
+
12
+ class Mapper(mapper.Mapper):
13
+ settings: provider_settings.Settings
14
+
15
+ def create_rate_request(self, payload: models.RateRequest) -> lib.Serializable:
16
+ return universal_provider.rate_request(payload, self.settings)
17
+
18
+ def create_tracking_request(
19
+ self, payload: models.TrackingRequest
20
+ ) -> lib.Serializable:
21
+ return provider.tracking_request(payload, self.settings)
22
+
23
+ def create_shipment_request(
24
+ self, payload: models.ShipmentRequest
25
+ ) -> lib.Serializable:
26
+ return provider.shipment_request(payload, self.settings)
27
+
28
+ def parse_rate_response(
29
+ self, response: lib.Deserializable[str]
30
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
31
+ return provider.parse_rate_response(response, self.settings)
32
+
33
+ def parse_shipment_response(
34
+ self, response: lib.Deserializable[str]
35
+ ) -> typing.Tuple[models.ShipmentDetails, typing.List[models.Message]]:
36
+ return provider.parse_shipment_response(response, self.settings)
37
+
38
+ def parse_tracking_response(
39
+ self, response: lib.Deserializable[str]
40
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
41
+ return provider.parse_tracking_response(response, self.settings)
@@ -0,0 +1,39 @@
1
+ """Karrio Colissimo client proxy."""
2
+
3
+ import karrio.lib as lib
4
+ import karrio.api.proxy as proxy
5
+ import karrio.providers.colissimo.utils as provider_utils
6
+ import karrio.mappers.colissimo.settings as provider_settings
7
+ import karrio.universal.mappers.rating_proxy as rating_proxy
8
+
9
+
10
+ class Proxy(rating_proxy.RatingMixinProxy, proxy.Proxy):
11
+ settings: provider_settings.Settings
12
+
13
+ def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
14
+ return super().get_rates(request)
15
+
16
+ def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
17
+ response = lib.request(
18
+ url=f"{self.settings.server_url}/generateLabel",
19
+ data=request.serialize(),
20
+ trace=self.trace_as("json"),
21
+ method="POST",
22
+ headers={"Content-Type": "application/json;charset=UTF-8"},
23
+ )
24
+
25
+ return lib.Deserializable(response, provider_utils.parse_response, request.ctx)
26
+
27
+ def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
28
+ idships = ",".join(request.serialize())
29
+ response = lib.request(
30
+ url=f"{self.settings.laposte_server_url}/idships/{idships}?lang={self.settings.connection_config.lang.state or 'fr_FR'}",
31
+ trace=self.trace_as("json"),
32
+ method="GET",
33
+ headers={
34
+ "accept": "application/json",
35
+ "X-Okapi-Key": self.settings.laposte_api_key,
36
+ },
37
+ )
38
+
39
+ return lib.Deserializable(response, lib.to_dict)
@@ -0,0 +1,35 @@
1
+ """Karrio Colissimo client settings."""
2
+
3
+ import attr
4
+ import typing
5
+ import jstruct
6
+ import karrio.core.models as models
7
+ import karrio.providers.colissimo.utils as provider_utils
8
+ import karrio.providers.colissimo.units as provider_units
9
+
10
+
11
+ @attr.s(auto_attribs=True)
12
+ class Settings(provider_utils.Settings):
13
+ """Colissimo connection settings."""
14
+
15
+ # required carrier specific properties
16
+ password: str
17
+ contract_number: str
18
+ laposte_api_key: str = None
19
+
20
+ # generic properties
21
+ id: str = None
22
+ test_mode: bool = False
23
+ carrier_id: str = "colissimo"
24
+ account_country_code: str = "FR"
25
+ metadata: dict = {}
26
+ config: dict = {}
27
+
28
+ services: typing.List[models.ServiceLevel] = jstruct.JList[models.ServiceLevel, False, dict(default=provider_units.DEFAULT_SERVICES)] # type: ignore
29
+
30
+ @property
31
+ def shipping_services(self) -> typing.List[models.ServiceLevel]:
32
+ if any(self.services or []):
33
+ return self.services
34
+
35
+ return provider_units.DEFAULT_SERVICES
@@ -0,0 +1,23 @@
1
+ import karrio.core.metadata as metadata
2
+ import karrio.mappers.colissimo as mappers
3
+ import karrio.providers.colissimo.units as units
4
+
5
+
6
+ METADATA = metadata.PluginMetadata(
7
+ status="beta",
8
+ id="colissimo",
9
+ label="Colissimo",
10
+ # Integrations
11
+ Mapper=mappers.Mapper,
12
+ Proxy=mappers.Proxy,
13
+ Settings=mappers.Settings,
14
+ # Data Units
15
+ options=units.ShippingOption,
16
+ services=units.ShippingService,
17
+ connection_configs=units.ConnectionConfig,
18
+ service_levels=units.DEFAULT_SERVICES,
19
+ # New fields
20
+ website="https://www.colissimo.entreprise.laposte.fr/en",
21
+ documentation="https://www.colissimo.entreprise.laposte.fr/en/tools-and-services",
22
+ description="Envoi de colis en France et dans le monde entier, livraison à domicile ou en point de retrait, Colissimo vous offre un choix de services qui facilitent votre quotidien.",
23
+ )
@@ -0,0 +1,9 @@
1
+ from karrio.providers.colissimo.utils import Settings
2
+ from karrio.providers.colissimo.shipment import (
3
+ parse_shipment_response,
4
+ shipment_request,
5
+ )
6
+ from karrio.providers.colissimo.tracking import (
7
+ parse_tracking_response,
8
+ tracking_request,
9
+ )
@@ -0,0 +1,52 @@
1
+ import karrio.schemas.colissimo.error_response as laposte
2
+ import typing
3
+ import karrio.lib as lib
4
+ import karrio.core.models as models
5
+ import karrio.providers.colissimo.utils as provider_utils
6
+
7
+
8
+ def parse_error_response(
9
+ response: dict,
10
+ settings: provider_utils.Settings,
11
+ **kwargs,
12
+ ) -> typing.List[models.Message]:
13
+ messages = response.get("json_info", {}).get("messages", [])
14
+
15
+ return [
16
+ models.Message(
17
+ carrier_id=settings.carrier_id,
18
+ carrier_name=settings.carrier_name,
19
+ code=msg.get("type"),
20
+ message=msg.get("messageContent"),
21
+ details={**kwargs},
22
+ )
23
+ for msg in messages
24
+ if msg.get("type") == "ERROR"
25
+ ]
26
+
27
+
28
+ def parse_laposte_error_response(
29
+ response: typing.Union[dict, typing.List[dict]],
30
+ settings: provider_utils.Settings,
31
+ **kwargs,
32
+ ) -> typing.List[models.Message]:
33
+ responses = response if isinstance(response, list) else [response]
34
+ errors = [
35
+ lib.to_object(laposte.ErrorResponse, res)
36
+ for res in responses
37
+ if (
38
+ not str(res.get("returnCode")).startswith("20")
39
+ or res.get("code") is not None
40
+ )
41
+ ]
42
+
43
+ return [
44
+ models.Message(
45
+ carrier_id=settings.carrier_id,
46
+ carrier_name=settings.carrier_name,
47
+ code=(error.returnCode or error.code),
48
+ message=(error.returnMessage or error.message),
49
+ details={**kwargs},
50
+ )
51
+ for error in errors
52
+ ]
@@ -0,0 +1,4 @@
1
+ from karrio.providers.colissimo.shipment.create import (
2
+ parse_shipment_response,
3
+ shipment_request,
4
+ )
@@ -0,0 +1,295 @@
1
+ import karrio.schemas.colissimo.label_request as colissimo
2
+ import karrio.schemas.colissimo.label_response as shipping
3
+ import typing
4
+ import base64
5
+ import karrio.lib as lib
6
+ import karrio.core.units as units
7
+ import karrio.core.models as models
8
+ import karrio.providers.colissimo.error as error
9
+ import karrio.providers.colissimo.utils as provider_utils
10
+ import karrio.providers.colissimo.units as provider_units
11
+
12
+
13
+ def parse_shipment_response(
14
+ _response: lib.Deserializable[dict],
15
+ settings: provider_utils.Settings,
16
+ ) -> typing.Tuple[typing.List[models.RateDetails], typing.List[models.Message]]:
17
+ response = _response.deserialize()
18
+
19
+ messages = error.parse_error_response(response, settings)
20
+ shipment = (
21
+ _extract_details(response, settings, _response.ctx)
22
+ if response.get("json_info", {}).get("labelV2Response") is not None
23
+ else None
24
+ )
25
+
26
+ return shipment, messages
27
+
28
+
29
+ def _extract_details(
30
+ data: dict,
31
+ settings: provider_utils.Settings,
32
+ ctx: dict = {},
33
+ ) -> models.ShipmentDetails:
34
+ shipment = lib.to_object(shipping.LabelResponse, data.get("json_info"))
35
+ label_type = ctx.get("label_type") or "PDF"
36
+ label = data.get("label")
37
+
38
+ if label_type != "ZPL":
39
+ label = base64.b64encode(bytes(label, encoding="raw_unicode_escape")).decode(
40
+ "utf-8"
41
+ )
42
+
43
+ return models.ShipmentDetails(
44
+ carrier_id=settings.carrier_id,
45
+ carrier_name=settings.carrier_name,
46
+ tracking_number=shipment.labelV2Response.parcelNumber,
47
+ shipment_identifier=shipment.labelV2Response.parcelNumber,
48
+ label_type="ZPL" if "ZPL" in label_type else "PDF",
49
+ docs=models.Documents(label=label),
50
+ meta=dict(
51
+ carrier_tracking_link=settings.tracking_url.format(
52
+ shipment.labelV2Response.parcelNumber
53
+ ),
54
+ request_uuid=data.get("uuid"),
55
+ pdfUrl=shipment.labelV2Response.pdfUrl,
56
+ parcelNumber=shipment.labelV2Response.parcelNumber,
57
+ parcelNumberPartner=shipment.labelV2Response.parcelNumberPartner,
58
+ ),
59
+ )
60
+
61
+
62
+ def shipment_request(
63
+ payload: models.ShipmentRequest,
64
+ settings: provider_utils.Settings,
65
+ ) -> lib.Serializable:
66
+ shipper = lib.to_address(payload.shipper)
67
+ recipient = lib.to_address(payload.recipient)
68
+ package = lib.to_packages(payload.parcels).single
69
+ service = provider_units.ShippingService.map(payload.service).value_or_key
70
+ options = lib.to_shipping_options(
71
+ payload.options,
72
+ package_options=package.options,
73
+ option_type=provider_units.ShippingOption,
74
+ )
75
+ customs = lib.to_customs_info(
76
+ payload.customs,
77
+ option_type=provider_units.ShippingOption,
78
+ shipper=shipper,
79
+ recipient=recipient,
80
+ )
81
+
82
+ request = colissimo.LabelRequest(
83
+ contractNumber=settings.contract_number,
84
+ password=settings.password,
85
+ outputFormat=colissimo.OutputFormat(
86
+ x=0,
87
+ y=0,
88
+ outputPrintingType=(
89
+ provider_units.LabelType.map(payload.label_type).value
90
+ or provider_utils.LabelType.PDF.value
91
+ ),
92
+ dematerialized=None,
93
+ returnType=None,
94
+ printCODDocument=None,
95
+ ),
96
+ letter=colissimo.Letter(
97
+ service=colissimo.Service(
98
+ productCode=service,
99
+ depositDate=lib.fdate(options.shipment_date.state, "%Y-%m-%d"),
100
+ mailBoxPicking=None,
101
+ mailBoxPickingDate=None,
102
+ vatCode=None,
103
+ vatPercentage=None,
104
+ vatAmount=None,
105
+ transportationAmount=options.declared_value.state,
106
+ totalAmount=options.declared_value.state,
107
+ orderNumber=None,
108
+ commercialName=shipper.company_name,
109
+ returnTypeChoice=None,
110
+ reseauPostal=0,
111
+ ),
112
+ parcel=colissimo.Parcel(
113
+ parcelNumber=package.parcel.reference_number,
114
+ insuranceAmount=None,
115
+ insuranceValue=options.insurance.state,
116
+ recommendationLevel=None,
117
+ weight=package.weight.KG,
118
+ nonMachinable=options.colissimo_non_machinable.state,
119
+ returnReceipt=options.colissimo_retun_receipt.state,
120
+ instructions=None,
121
+ pickupLocationId=None,
122
+ ftd=options.colissimo_ftd.state,
123
+ ddp=options.colissimo_ddp.state,
124
+ codamount=options.cash_on_delivery.state,
125
+ codcurrency=options.currency.state,
126
+ cod=options.cash_on_delivery.state is not None,
127
+ ),
128
+ customsDeclarations=(
129
+ colissimo.CustomsDeclarations(
130
+ includeCustomsDeclarations=True,
131
+ numberOfCopies=None,
132
+ contents=colissimo.Contents(
133
+ article=[
134
+ colissimo.Article(
135
+ description=item.description,
136
+ quantity=item.quantity,
137
+ weight=item.weight,
138
+ value=item.value_amount,
139
+ hsCode=item.hs_code,
140
+ originCountry=item.origin_country,
141
+ originCountryLabel=None,
142
+ currency=options.currency.state,
143
+ artref=item.metadata.get("artref", None),
144
+ originalIdent=None,
145
+ vatAmount=None,
146
+ customsFees=item.metadata.get("customs_fees", None),
147
+ )
148
+ for item in customs.commodities
149
+ ],
150
+ category=None,
151
+ original=None,
152
+ explanations=None,
153
+ ),
154
+ importersReference=None,
155
+ importersContact=shipper.contact,
156
+ officeOrigin=None,
157
+ comments=None,
158
+ description=customs.content_description,
159
+ invoiceNumber=customs.invoice,
160
+ licenceNumber=customs.options.license_number.state,
161
+ certificatNumber=customs.options.certificate_number.state,
162
+ importerAddress=colissimo.Address(
163
+ companyName=customs.duty_billing_address.company_name,
164
+ lastName=customs.duty_billing_address.person_name,
165
+ firstName=None,
166
+ line0=customs.duty_billing_address.suite,
167
+ line1=customs.duty_billing_address.street_number,
168
+ line2=customs.duty_billing_address.address_line1,
169
+ line3=customs.duty_billing_address.address_line2,
170
+ countryCode=customs.duty_billing_address.country_code,
171
+ countryLabel=customs.duty_billing_address.country_name,
172
+ city=customs.duty_billing_address.city,
173
+ zipCode=customs.duty_billing_address.postal_code,
174
+ phoneNumber=None,
175
+ mobileNumber=customs.duty_billing_address.phone_number,
176
+ doorCode1=None,
177
+ doorCode2=None,
178
+ intercom=None,
179
+ email=customs.duty_billing_address.email,
180
+ language=None,
181
+ stateOrProvinceCode=customs.duty_billing_address.state_code,
182
+ ),
183
+ )
184
+ if payload.customs is not None
185
+ else None
186
+ ),
187
+ sender=colissimo.Sender(
188
+ senderParcelRef=payload.reference or package.parcel.reference_number,
189
+ address=colissimo.Address(
190
+ companyName=shipper.company_name,
191
+ lastName=shipper.person_name,
192
+ firstName=None,
193
+ line0=shipper.suite,
194
+ line1=shipper.street_number,
195
+ line2=shipper.address_line1,
196
+ line3=shipper.address_line2,
197
+ countryCode=shipper.country_code,
198
+ countryLabel=shipper.country_name,
199
+ city=shipper.city,
200
+ zipCode=shipper.postal_code,
201
+ phoneNumber=shipper.phone_number,
202
+ mobileNumber=None,
203
+ doorCode1=None,
204
+ doorCode2=None,
205
+ intercom=None,
206
+ email=shipper.email,
207
+ language="FR",
208
+ stateOrProvinceCode=shipper.state_code,
209
+ ),
210
+ ),
211
+ addressee=colissimo.Addressee(
212
+ addresseeParcelRef=payload.reference or package.parcel.reference_number,
213
+ codeBarForReference=None,
214
+ serviceInfo=None,
215
+ promotionCode=None,
216
+ address=colissimo.Address(
217
+ companyName=recipient.company_name,
218
+ lastName=recipient.person_name,
219
+ firstName=None,
220
+ line0=recipient.suite,
221
+ line1=recipient.street_number,
222
+ line2=recipient.address_line1,
223
+ line3=recipient.address_line2,
224
+ countryCode=recipient.country_code,
225
+ countryLabel=recipient.country_name,
226
+ city=recipient.city,
227
+ zipCode=recipient.postal_code,
228
+ phoneNumber=recipient.phone_number,
229
+ mobileNumber=None,
230
+ doorCode1=None,
231
+ doorCode2=None,
232
+ intercom=None,
233
+ email=recipient.email,
234
+ language="FR",
235
+ stateOrProvinceCode=recipient.state_code,
236
+ ),
237
+ ),
238
+ codSenderAddress=(
239
+ colissimo.Address(
240
+ companyName=recipient.company_name,
241
+ lastName=recipient.person_name,
242
+ firstName=None,
243
+ line0=recipient.suite,
244
+ line1=recipient.street_number,
245
+ line2=recipient.address_line1,
246
+ line3=recipient.address_line2,
247
+ countryCode=recipient.country_code,
248
+ countryLabel=recipient.country_name,
249
+ city=recipient.city,
250
+ zipCode=recipient.postal_code,
251
+ phoneNumber=recipient.phone_number,
252
+ mobileNumber=None,
253
+ doorCode1=None,
254
+ doorCode2=None,
255
+ intercom=None,
256
+ email=recipient.email,
257
+ language="FR",
258
+ stateOrProvinceCode=recipient.state_code,
259
+ )
260
+ if options.cash_on_delivery.state is not None
261
+ else None
262
+ ),
263
+ uploadDocument=None,
264
+ features=colissimo.Features(printTrackingBarcode=True),
265
+ ),
266
+ fields=(
267
+ colissimo.Fields(
268
+ customField=[
269
+ colissimo.Field(
270
+ key=key,
271
+ value=value,
272
+ )
273
+ for key, value in [
274
+ ("LENGTH", package.length.value),
275
+ ("WIDTH", package.width.value),
276
+ ("HEIGHT", package.height.value),
277
+ ]
278
+ ]
279
+ )
280
+ if any(
281
+ [
282
+ package.length.value,
283
+ package.width.value,
284
+ package.height.value,
285
+ ]
286
+ )
287
+ else None
288
+ ),
289
+ )
290
+
291
+ return lib.Serializable(
292
+ request,
293
+ lib.to_dict,
294
+ dict(label_type=payload.label_type or "PDF"),
295
+ )
@@ -0,0 +1,79 @@
1
+ import karrio.schemas.colissimo.tracking_response as colissimo
2
+ import typing
3
+ import karrio.lib as lib
4
+ import karrio.core.units as units
5
+ import karrio.core.models as models
6
+ import karrio.providers.colissimo.error as error
7
+ import karrio.providers.colissimo.utils as provider_utils
8
+ import karrio.providers.colissimo.units as provider_units
9
+
10
+
11
+ def parse_tracking_response(
12
+ _response: lib.Deserializable[typing.Union[dict, typing.List[dict]]],
13
+ settings: provider_utils.Settings,
14
+ ) -> typing.Tuple[typing.List[models.TrackingDetails], typing.List[models.Message]]:
15
+ response = _response.deserialize()
16
+
17
+ responses = response if isinstance(response, list) else [response]
18
+ messages = error.parse_laposte_error_response(responses, settings)
19
+ tracking_details = [
20
+ _extract_details(res["shipment"], settings)
21
+ for res in responses
22
+ if str(res.get("returnCode")).startswith("20")
23
+ ]
24
+
25
+ return tracking_details, messages
26
+
27
+
28
+ def _extract_details(
29
+ data: dict,
30
+ settings: provider_utils.Settings,
31
+ ) -> models.TrackingDetails:
32
+ shipment = lib.to_object(colissimo.Shipment, data)
33
+ status = next(
34
+ (
35
+ status.name
36
+ for status in list(provider_units.TrackingStatus)
37
+ if shipment.event[0].code in status.value
38
+ ),
39
+ provider_units.TrackingStatus.in_transit.name,
40
+ )
41
+
42
+ return models.TrackingDetails(
43
+ carrier_id=settings.carrier_id,
44
+ carrier_name=settings.carrier_name,
45
+ tracking_number=shipment.idShip,
46
+ status=status,
47
+ events=[
48
+ models.TrackingEvent(
49
+ date=lib.fdate(event.date, "%Y-%m-%dT%H:%M:%S%z"),
50
+ description=event.label,
51
+ code=event.code,
52
+ time=lib.flocaltime(event.date, "%Y-%m-%dT%H:%M:%S%z"),
53
+ )
54
+ for event in shipment.event
55
+ ],
56
+ estimated_delivery=lib.fdate(shipment.deliveryDate, "%Y-%m-%dT%H:%M:%S%z"),
57
+ delivered=shipment.isFinal,
58
+ info=models.TrackingInfo(
59
+ carrier_tracking_link=shipment.url,
60
+ expected_delivery=lib.fdate(shipment.estimDate, "%Y-%m-%dT%H:%M:%S%z"),
61
+ shipment_service=shipment.product,
62
+ shipping_date=lib.fdate(shipment.entryDate, "%Y-%m-%dT%H:%M:%S%z"),
63
+ shipment_origin_country=getattr(
64
+ shipment.contextData, "originCountry", None
65
+ ),
66
+ shipment_destination_country=getattr(
67
+ shipment.contextData, "arrivalCountry", None
68
+ ),
69
+ ),
70
+ )
71
+
72
+
73
+ def tracking_request(
74
+ payload: models.TrackingRequest,
75
+ settings: provider_utils.Settings,
76
+ ) -> lib.Serializable:
77
+ request = payload.tracking_numbers
78
+
79
+ return lib.Serializable(request)
@@ -0,0 +1,169 @@
1
+ import karrio.lib as lib
2
+ import karrio.core.units as units
3
+ import karrio.core.models as models
4
+
5
+
6
+ class PackagingType(lib.StrEnum):
7
+ """Carrier specific packaging type"""
8
+
9
+ PACKAGE = "PACKAGE"
10
+
11
+ """ Unified Packaging type mapping """
12
+ envelope = PACKAGE
13
+ pak = PACKAGE
14
+ tube = PACKAGE
15
+ pallet = PACKAGE
16
+ small_box = PACKAGE
17
+ medium_box = PACKAGE
18
+ your_packaging = PACKAGE
19
+
20
+
21
+ class LabelType(lib.Enum):
22
+ ZPL_10x15_203dpi = "ZPL_10x15_203dpi"
23
+ ZPL_10x15_300dpi = "ZPL_10x15_300dpi"
24
+ DPL_10x15_203dpi = "DPL_10x15_203dpi"
25
+ DPL_10x15_300dpi = "DPL_10x15_300dpi"
26
+ PDF_10x15_300dpi = "PDF_10x15_300dpi"
27
+ PDF_A4_300dpi = "PDF_A4_300dpi"
28
+ ZPL_10x10_203dpi = "ZPL_10x10_203dpi"
29
+ ZPL_10x10_300dpi = "ZPL_10x10_300dpi"
30
+ DPL_10x10_203dpi = "DPL_10x10_203dpi"
31
+ DPL_10x10_300dpi = "DPL_10x10_300dpi"
32
+ PDF_10x10_300dpi = "PDF_10x10_300dpi"
33
+ PDF_10x12_300dpi = "PDF_10x12_300dpi"
34
+ ZPL_10x15_203dpi_UL = "ZPL_10x15_203dpi_UL"
35
+ ZPL_10x15_300dpi_UL = "ZPL_10x15_300dpi_UL"
36
+ DPL_10x15_203dpi_UL = "DPL_10x15_203dpi_UL"
37
+ DPL_10x15_300dpi_UL = "DPL_10x15_300dpi_UL"
38
+ PDF_10x15_300dpi_UL = "PDF_10x15_300dpi_UL"
39
+ PDF_A4_300dpi_UL = "PDF_A4_300dpi_UL"
40
+
41
+ """ Unified Label type mapping """
42
+ PDF = PDF_10x15_300dpi
43
+ ZPL = ZPL_10x10_300dpi
44
+
45
+
46
+ class ConnectionConfig(lib.Enum):
47
+ lang = lib.OptionEnum("lang", lib.units.create_enum("Lang", ["FR", "EN"]))
48
+
49
+
50
+ class ServiceName(lib.Enum):
51
+ """Carrier specific services"""
52
+
53
+ colissimo_home_without_signature = "Colissimo Home - without signature"
54
+ colissimo_home_with_signature = "Colissimo Home - with signature"
55
+ colissimo_eco_france = "Colissimo Eco France"
56
+ colissimo_return_france = "Colissimo Return France CORE 8R***"
57
+ colissimo_flash_without_signature = "Colissimo Flash – without signature"
58
+ colissimo_flash_with_signature = "Colissimo Flash – with signature"
59
+ colissimo_oversea_home_without_signature = "Colissimo Home OM - without signature"
60
+ colissimo_oversea_home_with_signature = "Colissimo Home OM - with signature"
61
+ colissimo_eco_om_without_signature = "Colissimo Eco OM - without signature"
62
+ colissimo_eco_om_with_signature = "Colissimo Eco OM - with signature"
63
+ colissimo_retour_om = "Colissimo Retour OM"
64
+ colissimo_home_international_without_signature = (
65
+ "Colissimo Home International - without signature***"
66
+ )
67
+ colissimo_home_international_with_signature = (
68
+ "Colissimo Home International - with signature***"
69
+ )
70
+ colissimo_return_international_to_france = (
71
+ "Colissimo Return International – Foreign country to France"
72
+ )
73
+ colissimo_return_international_from_france = (
74
+ "Colissimo Return International – France to foreign cournty"
75
+ )
76
+ colissimo_economical_big_export_offer = (
77
+ "Economical Big Export offer (test offer for China for a pilot customer)"
78
+ )
79
+ colissimo_out_of_home_national_international = (
80
+ "Colissimo Out Of Home National and International : **"
81
+ )
82
+ colissimo_out_of_home_post_office = "Colissimo - Out Of Home – at Post Office"
83
+ colissimo_out_of_home_pickup_points_station_lockers = (
84
+ "Colissimo - Out Of Home – at Pickup points or Pickup Station lockers"
85
+ )
86
+ colissimo_out_of_home_pickup_point = "Colissimo - Out Of Home – at pickup point"
87
+ colissimo_out_of_home_pickup_station_lockers = (
88
+ "Colissimo - Out Of Home – at Pickup Station lockers"
89
+ )
90
+
91
+
92
+ class ShippingService(lib.StrEnum):
93
+ """Carrier specific services"""
94
+
95
+ colissimo_home_without_signature = "DOM"
96
+ colissimo_home_with_signature = "DOS"
97
+ colissimo_eco_france = "CECO"
98
+ colissimo_return_france = "CORE"
99
+ colissimo_flash_without_signature = "COLR"
100
+ colissimo_flash_with_signature = "J+1"
101
+ colissimo_oversea_home_without_signature = "COM"
102
+ colissimo_oversea_home_with_signature = "CDS"
103
+ colissimo_eco_om_without_signature = "ECO"
104
+ colissimo_eco_om_with_signature = "ECOS"
105
+ colissimo_retour_om = "CORI"
106
+ colissimo_home_international_without_signature = colissimo_home_without_signature
107
+ colissimo_home_international_with_signature = colissimo_home_with_signature
108
+ colissimo_return_international_to_france = colissimo_retour_om
109
+ colissimo_return_international_from_france = "CORF"
110
+ colissimo_economical_big_export_offer = "ACCI"
111
+ colissimo_out_of_home_national_international = "HD"
112
+ colissimo_out_of_home_post_office = colissimo_out_of_home_national_international
113
+ colissimo_out_of_home_pickup_points_station_lockers = (
114
+ colissimo_out_of_home_national_international
115
+ )
116
+ colissimo_out_of_home_pickup_point = colissimo_out_of_home_national_international
117
+ colissimo_out_of_home_pickup_station_lockers = (
118
+ colissimo_out_of_home_national_international
119
+ )
120
+
121
+
122
+ class ShippingOption(lib.Enum):
123
+ """Carrier specific options"""
124
+
125
+ colissimo_insurance_value = lib.OptionEnum("insuranceValue", lib.to_money)
126
+ colissimo_cod_amount = lib.OptionEnum("CODAmount", lib.to_money)
127
+ colissimo_return_receipt = lib.OptionEnum("returnReceipt", bool)
128
+ colissimo_ftd = lib.OptionEnum("ftd", bool)
129
+ colissimo_non_machinable = lib.OptionEnum("nonMachinable", bool)
130
+ colissimo_ddp = lib.OptionEnum("ddp", bool)
131
+ colissimo_instructions = lib.OptionEnum("instructions")
132
+
133
+ """ Unified Option type mapping """
134
+ insurance = colissimo_insurance_value
135
+ cash_on_delivery = colissimo_cod_amount
136
+
137
+
138
+ def shipping_options_initializer(
139
+ options: dict,
140
+ package_options: units.ShippingOptions = None,
141
+ ) -> units.ShippingOptions:
142
+ """
143
+ Apply default values to the given options.
144
+ """
145
+
146
+ if package_options is not None:
147
+ options.update(package_options.content)
148
+
149
+ def items_filter(key: str) -> bool:
150
+ return key in ShippingOption # type: ignore
151
+
152
+ return units.ShippingOptions(options, ShippingOption, items_filter=items_filter)
153
+
154
+
155
+ class TrackingStatus(lib.Enum):
156
+ delivered = ["DI1"]
157
+ in_transit = [""]
158
+ out_for_delivery = ["MD2", "ET1"]
159
+
160
+
161
+ DEFAULT_SERVICES = [
162
+ models.ServiceLevel(
163
+ service_name="Colissimo Home - without signature",
164
+ service_code="colissimo_home_without_signature",
165
+ currency="EUR",
166
+ domicile=True,
167
+ zones=[models.ServiceZone(label="Zone 1", rate=0.0)],
168
+ ),
169
+ ]
@@ -0,0 +1,71 @@
1
+ import karrio.lib as lib
2
+ import karrio.core as core
3
+
4
+ JSON_START = """<jsonInfos>
5
+
6
+ {"""
7
+ JSON_END = """}
8
+ --uuid:"""
9
+ LABEL_START = """<label>
10
+
11
+ """
12
+ LABEL_END = r"""
13
+ --uuid:"""
14
+
15
+
16
+ class Settings(core.Settings):
17
+ """Colissimo connection settings."""
18
+
19
+ password: str
20
+ contract_number: str
21
+ laposte_api_key: str = None
22
+
23
+ account_country_code: str = "FR"
24
+ config: dict = {}
25
+
26
+ @property
27
+ def carrier_name(self):
28
+ return "colissimo"
29
+
30
+ @property
31
+ def server_url(self):
32
+ return "https://ws.colissimo.fr/sls-ws/SlsServiceWSRest/2.0"
33
+
34
+ @property
35
+ def laposte_server_url(self):
36
+ return "https://api.laposte.fr/suivi/v2"
37
+
38
+ @property
39
+ def tracking_url(self):
40
+ return "https://www.laposte.fr/outils/suivre-vos-envois?code={}"
41
+
42
+
43
+ def parse_response(response: str) -> dict:
44
+ """Parse colissimo multipart response."""
45
+
46
+ uuid = lib.failsafe(
47
+ lambda: response[
48
+ response.rfind("--uuid:") + len("--uuid:") : response.rfind("--")
49
+ ]
50
+ )
51
+ json_info = lib.failsafe(
52
+ lambda: response[
53
+ response.find(JSON_START) + len(JSON_START) : response.rfind(JSON_END)
54
+ ]
55
+ )
56
+ label = lib.failsafe(
57
+ lambda: (
58
+ response[
59
+ response.find(LABEL_START)
60
+ + len(LABEL_START) : response.rfind(LABEL_END)
61
+ ]
62
+ if "<label>" in response
63
+ else None
64
+ )
65
+ )
66
+
67
+ return dict(
68
+ uuid=uuid,
69
+ label=label,
70
+ json_info=lib.to_dict("{" + (json_info or "") + "}"),
71
+ )
File without changes
@@ -0,0 +1,13 @@
1
+ from attr import s
2
+ from typing import Optional
3
+
4
+
5
+ @s(auto_attribs=True)
6
+ class ErrorResponse:
7
+ returnCode: Optional[int] = None
8
+ returnMessage: Optional[str] = None
9
+ lang: Optional[str] = None
10
+ scope: Optional[str] = None
11
+ idShip: Optional[str] = None
12
+ code: Optional[str] = None
13
+ message: Optional[str] = None
@@ -0,0 +1,182 @@
1
+ from attr import s
2
+ from typing import Optional, List
3
+ from jstruct import JList, JStruct
4
+
5
+
6
+ @s(auto_attribs=True)
7
+ class Field:
8
+ key: Optional[str] = None
9
+ value: Optional[str] = None
10
+
11
+
12
+ @s(auto_attribs=True)
13
+ class Fields:
14
+ field: List[Field] = JList[Field]
15
+ customField: List[Field] = JList[Field]
16
+
17
+
18
+ @s(auto_attribs=True)
19
+ class Address:
20
+ companyName: Optional[str] = None
21
+ lastName: Optional[str] = None
22
+ firstName: Optional[str] = None
23
+ line0: Optional[str] = None
24
+ line1: Optional[str] = None
25
+ line2: Optional[str] = None
26
+ line3: Optional[str] = None
27
+ countryCode: Optional[str] = None
28
+ countryLabel: Optional[str] = None
29
+ city: Optional[str] = None
30
+ zipCode: Optional[str] = None
31
+ phoneNumber: Optional[str] = None
32
+ mobileNumber: Optional[str] = None
33
+ doorCode1: Optional[str] = None
34
+ doorCode2: Optional[str] = None
35
+ intercom: Optional[str] = None
36
+ email: Optional[str] = None
37
+ language: Optional[str] = None
38
+ stateOrProvinceCode: Optional[str] = None
39
+
40
+
41
+ @s(auto_attribs=True)
42
+ class Addressee:
43
+ addresseeParcelRef: Optional[str] = None
44
+ codeBarForReference: Optional[bool] = None
45
+ serviceInfo: Optional[str] = None
46
+ promotionCode: Optional[str] = None
47
+ address: Optional[Address] = JStruct[Address]
48
+
49
+
50
+ @s(auto_attribs=True)
51
+ class Article:
52
+ description: Optional[str] = None
53
+ quantity: Optional[int] = None
54
+ weight: Optional[int] = None
55
+ value: Optional[int] = None
56
+ hsCode: Optional[str] = None
57
+ originCountry: Optional[str] = None
58
+ originCountryLabel: Optional[str] = None
59
+ currency: Optional[str] = None
60
+ artref: Optional[str] = None
61
+ originalIdent: Optional[str] = None
62
+ vatAmount: Optional[int] = None
63
+ customsFees: Optional[int] = None
64
+
65
+
66
+ @s(auto_attribs=True)
67
+ class Category:
68
+ value: Optional[int] = None
69
+
70
+
71
+ @s(auto_attribs=True)
72
+ class Original:
73
+ originalIdent: Optional[str] = None
74
+ originalInvoiceNumber: Optional[str] = None
75
+ originalInvoiceDate: Optional[str] = None
76
+ originalParcelNumber: Optional[str] = None
77
+
78
+
79
+ @s(auto_attribs=True)
80
+ class Contents:
81
+ article: List[Article] = JList[Article]
82
+ category: Optional[Category] = JStruct[Category]
83
+ original: List[Original] = JList[Original]
84
+ explanations: Optional[str] = None
85
+
86
+
87
+ @s(auto_attribs=True)
88
+ class CustomsDeclarations:
89
+ includeCustomsDeclarations: Optional[bool] = None
90
+ numberOfCopies: Optional[int] = None
91
+ contents: Optional[Contents] = JStruct[Contents]
92
+ importersReference: Optional[str] = None
93
+ importersContact: Optional[str] = None
94
+ officeOrigin: Optional[str] = None
95
+ comments: Optional[str] = None
96
+ description: Optional[str] = None
97
+ invoiceNumber: Optional[str] = None
98
+ licenceNumber: Optional[str] = None
99
+ certificatNumber: Optional[str] = None
100
+ importerAddress: Optional[Address] = JStruct[Address]
101
+
102
+
103
+ @s(auto_attribs=True)
104
+ class Features:
105
+ printTrackingBarcode: Optional[bool] = None
106
+
107
+
108
+ @s(auto_attribs=True)
109
+ class Parcel:
110
+ parcelNumber: Optional[str] = None
111
+ insuranceAmount: Optional[int] = None
112
+ insuranceValue: Optional[int] = None
113
+ recommendationLevel: Optional[str] = None
114
+ weight: Optional[int] = None
115
+ nonMachinable: Optional[bool] = None
116
+ returnReceipt: Optional[bool] = None
117
+ instructions: Optional[str] = None
118
+ pickupLocationId: Optional[str] = None
119
+ ftd: Optional[bool] = None
120
+ ddp: Optional[bool] = None
121
+ codamount: Optional[int] = None
122
+ codcurrency: Optional[str] = None
123
+ cod: Optional[bool] = None
124
+
125
+
126
+ @s(auto_attribs=True)
127
+ class Sender:
128
+ senderParcelRef: Optional[str] = None
129
+ address: Optional[Address] = JStruct[Address]
130
+
131
+
132
+ @s(auto_attribs=True)
133
+ class Service:
134
+ productCode: Optional[str] = None
135
+ depositDate: Optional[str] = None
136
+ mailBoxPicking: Optional[bool] = None
137
+ mailBoxPickingDate: Optional[str] = None
138
+ vatCode: Optional[int] = None
139
+ vatPercentage: Optional[int] = None
140
+ vatAmount: Optional[int] = None
141
+ transportationAmount: Optional[int] = None
142
+ totalAmount: Optional[int] = None
143
+ orderNumber: Optional[str] = None
144
+ commercialName: Optional[str] = None
145
+ returnTypeChoice: Optional[int] = None
146
+ reseauPostal: Optional[str] = None
147
+
148
+
149
+ @s(auto_attribs=True)
150
+ class UploadDocument:
151
+ documentContent: List[str] = []
152
+
153
+
154
+ @s(auto_attribs=True)
155
+ class Letter:
156
+ service: Optional[Service] = JStruct[Service]
157
+ parcel: Optional[Parcel] = JStruct[Parcel]
158
+ customsDeclarations: Optional[CustomsDeclarations] = JStruct[CustomsDeclarations]
159
+ sender: Optional[Sender] = JStruct[Sender]
160
+ addressee: Optional[Addressee] = JStruct[Addressee]
161
+ codSenderAddress: Optional[Address] = JStruct[Address]
162
+ uploadDocument: Optional[UploadDocument] = JStruct[UploadDocument]
163
+ features: Optional[Features] = JStruct[Features]
164
+
165
+
166
+ @s(auto_attribs=True)
167
+ class OutputFormat:
168
+ x: Optional[int] = None
169
+ y: Optional[int] = None
170
+ outputPrintingType: Optional[str] = None
171
+ dematerialized: Optional[bool] = None
172
+ returnType: Optional[str] = None
173
+ printCODDocument: Optional[bool] = None
174
+
175
+
176
+ @s(auto_attribs=True)
177
+ class LabelRequest:
178
+ contractNumber: Optional[str] = None
179
+ password: Optional[str] = None
180
+ outputFormat: Optional[OutputFormat] = JStruct[OutputFormat]
181
+ letter: Optional[Letter] = JStruct[Letter]
182
+ fields: Optional[Fields] = JStruct[Fields]
@@ -0,0 +1,26 @@
1
+ from attr import s
2
+ from typing import Optional, Any, List
3
+ from jstruct import JList, JStruct
4
+
5
+
6
+ @s(auto_attribs=True)
7
+ class LabelV2Response:
8
+ parcelNumber: Optional[str] = None
9
+ parcelNumberPartner: Optional[str] = None
10
+ pdfUrl: Any = None
11
+ fields: Any = None
12
+
13
+
14
+ @s(auto_attribs=True)
15
+ class Message:
16
+ id: Optional[int] = None
17
+ type: Optional[str] = None
18
+ messageContent: Optional[str] = None
19
+ replacementValues: List[Any] = []
20
+
21
+
22
+ @s(auto_attribs=True)
23
+ class LabelResponse:
24
+ messages: List[Message] = JList[Message]
25
+ labelXmlV2Reponse: Any = None
26
+ labelV2Response: Optional[LabelV2Response] = JStruct[LabelV2Response]
@@ -0,0 +1,7 @@
1
+ from attr import s
2
+ from typing import Optional
3
+
4
+
5
+ @s(auto_attribs=True)
6
+ class TrackingRequest:
7
+ noSuivi: Optional[str] = None
@@ -0,0 +1,61 @@
1
+ from attr import s
2
+ from typing import Optional, List
3
+ from jstruct import JStruct, JList
4
+
5
+
6
+ @s(auto_attribs=True)
7
+ class DeliveryChoice:
8
+ deliveryChoice: Optional[int] = None
9
+
10
+
11
+ @s(auto_attribs=True)
12
+ class ContextData:
13
+ deliveryChoice: Optional[DeliveryChoice] = JStruct[DeliveryChoice]
14
+ originCountry: Optional[str] = None
15
+ arrivalCountry: Optional[str] = None
16
+
17
+
18
+ @s(auto_attribs=True)
19
+ class Event:
20
+ code: Optional[str] = None
21
+ label: Optional[str] = None
22
+ date: Optional[str] = None
23
+
24
+
25
+ @s(auto_attribs=True)
26
+ class Timeline:
27
+ shortLabel: Optional[str] = None
28
+ longLabel: Optional[str] = None
29
+ id: Optional[int] = None
30
+ country: Optional[str] = None
31
+ status: Optional[bool] = None
32
+ type: Optional[int] = None
33
+ date: Optional[str] = None
34
+
35
+
36
+ @s(auto_attribs=True)
37
+ class Shipment:
38
+ idShip: Optional[str] = None
39
+ holder: Optional[int] = None
40
+ product: Optional[str] = None
41
+ isFinal: Optional[bool] = None
42
+ deliveryDate: Optional[str] = None
43
+ entryDate: Optional[str] = None
44
+ timeline: List[Timeline] = JList[Timeline]
45
+ event: List[Event] = JList[Event]
46
+ contextData: Optional[ContextData] = JStruct[ContextData]
47
+ estimDate: Optional[str] = None
48
+ url: Optional[str] = None
49
+
50
+
51
+ @s(auto_attribs=True)
52
+ class Response:
53
+ lang: Optional[str] = None
54
+ scope: Optional[str] = None
55
+ returnCode: Optional[int] = None
56
+ shipment: Optional[Shipment] = JStruct[Shipment]
57
+
58
+
59
+ @s(auto_attribs=True)
60
+ class TrackingResponse:
61
+ responses: List[Response] = JList[Response]
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: karrio_colissimo
3
+ Version: 2025.5rc1
4
+ Summary: Karrio - Colissimo Shipping Extension
5
+ Author-email: karrio <hello@karrio.io>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/karrioapi/karrio
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: karrio
14
+
15
+
16
+ # karrio.colissimo
17
+
18
+ This package is a Colissimo extension of the [karrio](https://pypi.org/project/karrio) multi carrier shipping SDK.
19
+
20
+ ## Requirements
21
+
22
+ `Python 3.7+`
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install karrio.colissimo
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ```python
33
+ import karrio.sdk as karrio
34
+ from karrio.mappers.colissimo.settings import Settings
35
+
36
+
37
+ # Initialize a carrier gateway
38
+ colissimo = karrio.gateway["colissimo"].create(
39
+ Settings(
40
+ ...
41
+ )
42
+ )
43
+ ```
44
+
45
+ Check the [Karrio Mutli-carrier SDK docs](https://docs.karrio.io) for Shipping API requests
@@ -0,0 +1,23 @@
1
+ karrio/mappers/colissimo/__init__.py,sha256=ifjuTREZiGGSrlhTYVKx7UkbRZNqr6m9R_LTA0_jkQ4,155
2
+ karrio/mappers/colissimo/mapper.py,sha256=-TCZysxQ_67Nz48BFM0qCVCBpkSVRoC1qH-nzbPHHog,1599
3
+ karrio/mappers/colissimo/proxy.py,sha256=B7jkoqRBBUy3ZIFTjpra40dSqw9O2B9QmzBQm9x6mx8,1532
4
+ karrio/mappers/colissimo/settings.py,sha256=-NqBwwBDqYWlHoxCRHpIBn3GFwlGgHgtgrDNTN8hd00,1011
5
+ karrio/plugins/colissimo/__init__.py,sha256=tMZGuJQqNTQe8HULxGBm18Xgd_pjpSbIQUycEWtYktE,868
6
+ karrio/providers/colissimo/__init__.py,sha256=zGCjtpOeH6IB2BtmFChbuMGFRPcEwxLshRf4AgjPEec,260
7
+ karrio/providers/colissimo/error.py,sha256=U53tPupLsDcO9Jdbk9niz0PIgCE36hv7LqlfiUJzdwY,1506
8
+ karrio/providers/colissimo/tracking.py,sha256=4_r5F4jgC8j-be4Sx5RXSGuWbpO36pH_TAcYmUyBn-w,2747
9
+ karrio/providers/colissimo/units.py,sha256=oBKh9h_87TD3uwQUWNgM8FfDv0KuhSGRnXWFLQZRUPI,6317
10
+ karrio/providers/colissimo/utils.py,sha256=rWfvBXJ53QS6nSC8FetWZ1SmN3JiwUApvROdwcrZtL4,1555
11
+ karrio/providers/colissimo/shipment/__init__.py,sha256=wNMIG1QNPaIopEfjD05wLqUjmRhYj4T05kFONkivFzA,110
12
+ karrio/providers/colissimo/shipment/create.py,sha256=eD0jAD9hHLGy_YCEf3ELoAwq_PZtKumUoSBhciBrDto,12117
13
+ karrio/schemas/colissimo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ karrio/schemas/colissimo/error_response.py,sha256=iBYZjvqI6JKwQCfZBObNNfIBG9iUu9Pf-ZgcTicsrd8,330
15
+ karrio/schemas/colissimo/label_request.py,sha256=pSMOWHrz8ulakt3-HaN86oY6j9bSjIMfla2zQ9sZ1sc,5338
16
+ karrio/schemas/colissimo/label_response.py,sha256=3g2cwUNTalKVCUmvbpHhGlPdKMIey8eam6xjhfTP9Wo,647
17
+ karrio/schemas/colissimo/tracking_request.py,sha256=K0S51-ktf4rfXNGKdj-basUAmLALwH8emY_VC_vmpnY,128
18
+ karrio/schemas/colissimo/tracking_response.py,sha256=U3W2TbdT5ld_ZlZIMXRY62ScCEZ2nsFdcQPTFFNudl0,1525
19
+ karrio_colissimo-2025.5rc1.dist-info/METADATA,sha256=DOuYP6F130txZtuhVq8jszQ_vE75djHT8MYH9QppqGk,1008
20
+ karrio_colissimo-2025.5rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ karrio_colissimo-2025.5rc1.dist-info/entry_points.txt,sha256=5D8gMo5ptusxiT7anI2epCZNsG5N56UXv83pmZvvvjo,63
22
+ karrio_colissimo-2025.5rc1.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
23
+ karrio_colissimo-2025.5rc1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [karrio.plugins]
2
+ colissimo = karrio.plugins.colissimo:METADATA
@@ -0,0 +1,3 @@
1
+ dist
2
+ karrio
3
+ schemas