prestashop-webservice 0.3.0__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,448 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any
3
+
4
+
5
+ @dataclass
6
+ class OrderData:
7
+ """Order resource data."""
8
+
9
+ id: int
10
+ # Core fields
11
+ id_customer: int | None = None
12
+ id_cart: int | None = None
13
+ id_currency: int | None = None
14
+ id_lang: int | None = None
15
+ id_address_delivery: int | None = None
16
+ id_address_invoice: int | None = None
17
+ id_carrier: int | None = None
18
+ id_shop: int | None = None
19
+ id_shop_group: int | None = None
20
+ current_state: int | None = None
21
+ reference: str | None = None
22
+ secure_key: str | None = None
23
+ payment: str | None = None
24
+ module: str | None = None
25
+ # Totals
26
+ total_paid: str | None = None
27
+ total_paid_tax_incl: str | None = None
28
+ total_paid_tax_excl: str | None = None
29
+ total_paid_real: str | None = None
30
+ total_products: str | None = None
31
+ total_products_wt: str | None = None
32
+ total_shipping: str | None = None
33
+ total_shipping_tax_incl: str | None = None
34
+ total_shipping_tax_excl: str | None = None
35
+ total_discounts: str | None = None
36
+ total_discounts_tax_incl: str | None = None
37
+ total_discounts_tax_excl: str | None = None
38
+ total_wrapping: str | None = None
39
+ total_wrapping_tax_incl: str | None = None
40
+ total_wrapping_tax_excl: str | None = None
41
+ # Other fields
42
+ carrier_tax_rate: str | None = None
43
+ conversion_rate: str | None = None
44
+ round_mode: int | None = None
45
+ round_type: int | None = None
46
+ invoice_number: str | None = None
47
+ invoice_date: str | None = None
48
+ delivery_number: str | None = None
49
+ delivery_date: str | None = None
50
+ shipping_number: str | None = None
51
+ note: str | None = None
52
+ valid: int | None = None
53
+ date_add: str | None = None
54
+ date_upd: str | None = None
55
+ associations: dict[str, Any] | None = None
56
+
57
+ def __post_init__(self, **kwargs):
58
+ """Ignore extra fields from API."""
59
+ pass
60
+
61
+
62
+ @dataclass
63
+ class CustomerData:
64
+ """Customer resource data."""
65
+
66
+ id: int
67
+ # Core fields
68
+ id_default_group: int | None = None
69
+ id_lang: int | None = None
70
+ id_shop: int | None = None
71
+ id_shop_group: int | None = None
72
+ email: str | None = None
73
+ firstname: str | None = None
74
+ lastname: str | None = None
75
+ active: int | None = None
76
+ date_add: str | None = None
77
+ date_upd: str | None = None
78
+ # Optional fields
79
+ id_gender: int | None = None
80
+ birthday: str | None = None
81
+ newsletter: int | None = None
82
+ optin: int | None = None
83
+ website: str | None = None
84
+ company: str | None = None
85
+ siret: str | None = None
86
+ ape: str | None = None
87
+ outstanding_allow_amount: str | None = None
88
+ max_payment_days: int | None = None
89
+ note: str | None = None
90
+ secure_key: str | None = None
91
+ # Extra fields from API
92
+ passwd: str | None = None
93
+ last_passwd_gen: str | None = None
94
+ newsletter_date_add: str | None = None
95
+ reset_password_validity: str | None = None
96
+ associations: dict[str, Any] | None = None
97
+
98
+ def __post_init__(self, **kwargs):
99
+ """Ignore extra fields from API."""
100
+ pass
101
+
102
+
103
+ @dataclass
104
+ class ProductData:
105
+ """Product resource data."""
106
+
107
+ id: int
108
+ # Core fields
109
+ id_manufacturer: int | None = None
110
+ id_supplier: int | None = None
111
+ id_category_default: int | None = None
112
+ id_shop_default: int | None = None
113
+ id_tax_rules_group: int | None = None
114
+ id_default_image: int | None = None # Default product image
115
+ id_default_combination: int | None = None # Default product combination
116
+ reference: str | None = None
117
+ name: str | None = None
118
+ manufacturer_name: str | None = None
119
+ price: str | None = None
120
+ active: int | None = None
121
+ date_add: str | None = None
122
+ date_upd: str | None = None
123
+ # Optional fields
124
+ ean13: str | None = None
125
+ isbn: str | None = None
126
+ upc: str | None = None
127
+ mpn: str | None = None
128
+ supplier_reference: str | None = None
129
+ location: str | None = None
130
+ width: str | None = None
131
+ height: str | None = None
132
+ depth: str | None = None
133
+ weight: str | None = None
134
+ quantity: int | None = None
135
+ description: str | None = None
136
+ description_short: str | None = None
137
+ available_for_order: int | None = None
138
+ condition: str | None = None
139
+ show_price: int | None = None
140
+ indexed: int | None = None
141
+ visibility: str | None = None
142
+ cache_default_attribute: int | None = None
143
+ advanced_stock_management: int | None = None
144
+ available_date: str | None = None
145
+ type: str | None = None
146
+ state: int | None = None
147
+ # Extra fields from API
148
+ additional_shipping_cost: str | None = None
149
+ ecotax: str | None = None
150
+ link_rewrite: str | None = None
151
+ meta_description: str | None = None
152
+ meta_title: str | None = None # Meta title for SEO
153
+ minimal_quantity: int | None = None # Minimum purchase quantity
154
+ pack_stock_type: int | None = None # Stock type for packs
155
+ position_in_category: int | None = None
156
+ product_type: str | None = None
157
+ redirect_type: str | None = None # Redirect type (404, etc)
158
+ unit_price: str | None = None
159
+ unit_price_ratio: str | None = None
160
+ wholesale_price: str | None = None
161
+ additional_delivery_times: int | None = None # Delivery times option
162
+ associations: dict[str, Any] | None = None
163
+ cache_is_pack: int | None = None
164
+ low_stock_threshold: int | None = None
165
+ unity: str | None = None
166
+
167
+ def __post_init__(self, **kwargs):
168
+ """Ignore extra fields from API."""
169
+ pass
170
+
171
+
172
+ @dataclass
173
+ class AddressData:
174
+ """Address resource data."""
175
+
176
+ id: int
177
+ # All fields optional except id
178
+ id_customer: int | None = None
179
+ id_country: int | None = None
180
+ alias: str | None = None
181
+ lastname: str | None = None
182
+ firstname: str | None = None
183
+ address1: str | None = None
184
+ city: str | None = None
185
+ postcode: str | None = None
186
+ date_add: str | None = None
187
+ date_upd: str | None = None
188
+ id_manufacturer: int | None = None
189
+ id_supplier: int | None = None
190
+ id_warehouse: int | None = None
191
+ id_state: int | None = None
192
+ company: str | None = None
193
+ vat_number: str | None = None
194
+ address2: str | None = None
195
+ phone: str | None = None
196
+ phone_mobile: str | None = None
197
+ dni: str | None = None
198
+ deleted: int | None = None
199
+ other: str | None = None
200
+
201
+ def __post_init__(self, **kwargs):
202
+ """Ignore extra fields from API."""
203
+ pass
204
+
205
+
206
+ @dataclass
207
+ class CountryData:
208
+ """Country resource data."""
209
+
210
+ id: int
211
+ # All fields optional except id
212
+ id_zone: int | None = None
213
+ iso_code: str | None = None
214
+ name: str | None = None
215
+ active: int | None = None
216
+ call_prefix: int | None = None
217
+ contains_states: int | None = None
218
+ need_identification_number: int | None = None
219
+ need_zip_code: int | None = None
220
+ zip_code_format: str | None = None
221
+ display_tax_label: int | None = None
222
+ id_currency: int | None = None
223
+
224
+ def __post_init__(self, **kwargs):
225
+ """Ignore extra fields from API."""
226
+ pass
227
+
228
+
229
+ @dataclass
230
+ class CarrierData:
231
+ """Carrier resource data."""
232
+
233
+ id: int
234
+ deleted: int | None = None
235
+ is_module: int | None = None
236
+ id_tax_rules_group: int | None = None
237
+ id_reference: int | None = None
238
+ name: str | None = None
239
+ active: int | None = None
240
+ is_free: int | None = None
241
+ url: str | None = None
242
+ shipping_handling: int | None = None
243
+ shipping_external: int | None = None
244
+ range_behavior: int | None = None
245
+ shipping_method: int | None = None
246
+ max_width: int | None = None
247
+ max_height: int | None = None
248
+ max_depth: int | None = None
249
+ max_weight: str | None = None
250
+ grade: int | None = None
251
+ external_module_name: str | None = None
252
+ need_range: int | None = None
253
+ position: int | None = None
254
+ delay: str | dict[str, Any] | None = None
255
+
256
+ def __post_init__(self, **kwargs):
257
+ """Ignore extra fields from API."""
258
+ pass
259
+
260
+
261
+ @dataclass
262
+ class OrderCarrierData:
263
+ """Order carrier resource data."""
264
+
265
+ id: int
266
+ # All fields optional except id
267
+ id_order: int | None = None
268
+ id_carrier: int | None = None
269
+ id_order_invoice: int | None = None
270
+ weight: str | None = None
271
+ shipping_cost_tax_excl: str | None = None
272
+ shipping_cost_tax_incl: str | None = None
273
+ tracking_number: str | None = None
274
+ date_add: str | None = None
275
+
276
+ def __post_init__(self, **kwargs):
277
+ """Ignore extra fields from API."""
278
+ pass
279
+
280
+
281
+ @dataclass
282
+ class OrderHistoryData:
283
+ """Order history resource data."""
284
+
285
+ id: int
286
+ # All fields optional except id
287
+ id_order: int | None = None
288
+ id_order_state: int | None = None
289
+ id_employee: int | None = None
290
+ date_add: str | None = None
291
+
292
+ def __post_init__(self, **kwargs):
293
+ """Ignore extra fields from API."""
294
+ pass
295
+
296
+
297
+ @dataclass
298
+ class OrderStateData:
299
+ """Order state resource data."""
300
+
301
+ id: int
302
+ # All fields optional except id
303
+ name: str | None = None
304
+ color: str | None = None
305
+ unremovable: int | None = None
306
+ send_email: int | None = None
307
+ module_name: str | None = None
308
+ invoice: int | None = None
309
+ # Extra from API
310
+ template: str | None = None
311
+ # Fields that may not come
312
+ deleted: int | None = None
313
+ delivery: int | None = None
314
+ hidden: int | None = None
315
+ logable: int | None = None
316
+ paid: int | None = None
317
+ pdf_delivery: int | None = None
318
+ pdf_invoice: int | None = None
319
+ shipped: int | None = None
320
+
321
+ def __post_init__(self, **kwargs):
322
+ """Ignore extra fields from API."""
323
+ pass
324
+
325
+
326
+ @dataclass
327
+ class StateData:
328
+ """State/Province resource data."""
329
+
330
+ id: int
331
+ # All fields optional except id
332
+ id_country: int | None = None
333
+ id_zone: int | None = None
334
+ name: str | None = None
335
+ iso_code: str | None = None
336
+ active: int | None = None
337
+
338
+ def __post_init__(self, **kwargs):
339
+ """Ignore extra fields from API."""
340
+ pass
341
+
342
+
343
+ @dataclass
344
+ class ImageProductData:
345
+ """Product image resource data."""
346
+
347
+ id: int
348
+ # All fields optional except id
349
+ id_product: int | None = None
350
+ position: int | None = None
351
+ cover: int | None = None
352
+ legend: str | None = None
353
+
354
+ def __post_init__(self, **kwargs):
355
+ """Ignore extra fields from API."""
356
+ pass
357
+
358
+
359
+ @dataclass
360
+ class CombinationData:
361
+ """Combination resource data."""
362
+
363
+ id: int
364
+ # All fields optional except id
365
+ id_product: int | None = None
366
+ ean13: str | None = None
367
+ reference: str | None = None
368
+ wholesale_price: str | None = None
369
+ price: str | None = None
370
+ ecotax: str | None = None
371
+ weight: str | None = None
372
+ unit_price_impact: str | None = None
373
+ minimal_quantity: int | None = None
374
+ low_stock_threshold: int | None = None
375
+ default_on: int | None = None
376
+ available_date: str | None = None
377
+ associations: dict[str, Any] | None = None
378
+
379
+ def __post_init__(self, **kwargs):
380
+ """Ignore extra fields from API."""
381
+ pass
382
+
383
+
384
+ @dataclass
385
+ class CustomerMessageData:
386
+ """Customer message resource data."""
387
+
388
+ id: int
389
+ id_customer_thread: int | None = None
390
+ id_employee: int | None = None
391
+ message: str | None = None
392
+ file_name: str | None = None
393
+ ip_address: str | None = None
394
+ user_agent: str | None = None
395
+ date_add: str | None = None
396
+ date_upd: str | None = None
397
+ private: int | None = None
398
+ read: int | None = None
399
+
400
+ def __post_init__(self, **kwargs):
401
+ """Ignore extra fields from API."""
402
+ pass
403
+
404
+
405
+ @dataclass
406
+ class OrderDetailData:
407
+ """Order detail resource data."""
408
+
409
+ id: int
410
+ id_order: int | None = None
411
+ product_id: int | None = None
412
+ product_attribute_id: int | None = None
413
+ product_name: str | None = None
414
+ product_quantity: int | None = None
415
+ product_price: str | None = None
416
+ product_reference: str | None = None
417
+ total_price_tax_incl: str | None = None
418
+ total_price_tax_excl: str | None = None
419
+ unit_price_tax_incl: str | None = None
420
+ unit_price_tax_excl: str | None = None
421
+ original_product_price: str | None = None
422
+
423
+ def __post_init__(self, **kwargs):
424
+ """Ignore extra fields from API."""
425
+ pass
426
+
427
+
428
+ @dataclass
429
+ class CustomerThreadData:
430
+ """Customer thread resource data."""
431
+
432
+ id: int
433
+ id_shop: int | None = None
434
+ id_lang: int | None = None
435
+ id_contact: int | None = None
436
+ id_customer: int | None = None
437
+ id_order: int | None = None
438
+ id_product: int | None = None
439
+ status: str | None = None
440
+ email: str | None = None
441
+ token: str | None = None
442
+ date_add: str | None = None
443
+ date_upd: str | None = None
444
+ associations: dict[str, Any] | None = None
445
+
446
+ def __post_init__(self, **kwargs):
447
+ """Ignore extra fields from API."""
448
+ pass
@@ -0,0 +1,114 @@
1
+ from prestashop_webservice.base_model import BaseModel
2
+ from prestashop_webservice.models import OrderData
3
+ from prestashop_webservice.params import Params
4
+
5
+
6
+ class Order(BaseModel):
7
+ """Complex queries for orders endpoint."""
8
+
9
+ _data_class = OrderData
10
+
11
+ def get_by_id(self, order_id: str) -> OrderData:
12
+ """Get order by ID."""
13
+ return self._query(f"orders/{order_id}", None, "order")
14
+
15
+ def exists(self, order_id: str) -> bool:
16
+ """Check if order exists by ID."""
17
+ try:
18
+ self.get_by_id(order_id)
19
+ return True
20
+ except Exception:
21
+ return False
22
+
23
+ def get_by_reference(self, reference: str) -> OrderData | None:
24
+ """Get order by reference."""
25
+ params = Params(
26
+ filter={"reference": reference},
27
+ display=["id", "reference", "total_paid", "current_state", "id_customer"],
28
+ )
29
+ orders: list[OrderData] = self._query("orders", params, "orders")
30
+ return orders[0] if orders else None
31
+
32
+ def get_by_customer(self, customer_id: str, limit: int = 999) -> list[OrderData]:
33
+ """Get all orders for a customer."""
34
+ params = Params(
35
+ filter={"id_customer": customer_id},
36
+ display=["id", "id_customer", "date_add", "total_paid", "reference"],
37
+ limit=limit,
38
+ )
39
+ return self._query("orders", params, "orders")
40
+
41
+ def get_latest_by_customer(self, customer_id: str) -> OrderData | None:
42
+ """Get the most recent order for a customer."""
43
+ params = Params(
44
+ filter={"id_customer": customer_id},
45
+ display=["id", "id_customer", "date_add", "total_paid", "reference"],
46
+ limit=1,
47
+ )
48
+ orders: list[OrderData] = self._query("orders", params, "orders")
49
+ return orders[0] if orders else None
50
+
51
+ def get_recent(self, limit: int = 10) -> list[OrderData]:
52
+ """Get recent orders."""
53
+ params = Params(
54
+ display=["id", "reference", "total_paid", "date_add", "id_customer"],
55
+ limit=limit,
56
+ )
57
+ return self._query("orders", params, "orders") # type: ignore
58
+
59
+ def get_by_status(self, status_id: str, limit: int = 100) -> list[OrderData]:
60
+ """Get orders by status."""
61
+ params = Params(
62
+ filter={"current_state": status_id},
63
+ display=["id", "current_state", "reference", "date_add"],
64
+ limit=limit,
65
+ )
66
+ return self._query("orders", params, "orders")
67
+
68
+ def get_shipped_orders(
69
+ self, start_date: str = "2025-11-10", end_date: str = "3500-12-11"
70
+ ) -> list[OrderData]:
71
+ """Get orders with shipped status."""
72
+ params = Params(
73
+ filter={"current_state": "4", "date_add": f"[{start_date},{end_date}]"},
74
+ display=["id", "shipping_number"],
75
+ date=True,
76
+ )
77
+ return self._query("orders", params, "orders")
78
+
79
+ def get_total_wrapping(self, order_id: str) -> str | None:
80
+ """Get total wrapping (donation) for an order."""
81
+ order = self.get_by_id(order_id)
82
+ return order.total_wrapping
83
+
84
+ def get_total_shipping(self, order_id: str) -> str | None:
85
+ """Get total shipping cost for an order."""
86
+ order = self.get_by_id(order_id)
87
+ return order.total_shipping
88
+
89
+ def get_total_discounts(self, order_id: str) -> str | None:
90
+ """Get total discounts for an order."""
91
+ order = self.get_by_id(order_id)
92
+ return order.total_discounts
93
+
94
+ def get_all_orders_since(
95
+ self, start_date: str = "2025-11-10", end_date: str = "3500-12-11"
96
+ ) -> list[OrderData]:
97
+ """Get all orders since a specific date."""
98
+ params = Params(
99
+ filter={"date_add": f"[{start_date},{end_date}]"},
100
+ display=["id", "reference", "date_add", "current_state", "total_paid"],
101
+ date=True,
102
+ )
103
+ return self._query("orders", params, "orders")
104
+
105
+ def get_orders_available_at_pickup_point(
106
+ self, start_date: str = "2025-11-10", end_date: str = "3500-12-11"
107
+ ) -> list[OrderData]:
108
+ """Get orders with shipped status."""
109
+ params = Params(
110
+ filter={"current_state": "41", "date_add": f"[{start_date},{end_date}]"},
111
+ display=["id", "shipping_number"],
112
+ date=True,
113
+ )
114
+ return self._query("orders", params, "orders")
@@ -0,0 +1,33 @@
1
+ from prestashop_webservice.base_model import BaseModel
2
+ from prestashop_webservice.models import OrderCarrierData
3
+ from prestashop_webservice.params import Params
4
+
5
+
6
+ class OrderCarrier(BaseModel):
7
+ """Complex queries for order_carriers endpoint."""
8
+
9
+ _data_class = OrderCarrierData
10
+
11
+ def get_by_order(self, order_id: str) -> list[OrderCarrierData]:
12
+ """Get carriers for a specific order."""
13
+ params = Params(
14
+ filter={"id_order": order_id},
15
+ display=["id", "id_order", "tracking_number"],
16
+ )
17
+ return self._query("order_carriers", params, "order_carriers")
18
+
19
+ def get_by_tracking(self, tracking: str) -> list[OrderCarrierData]:
20
+ """Find order carriers by tracking number."""
21
+ params = Params(
22
+ filter={"tracking_number": tracking},
23
+ display=["id", "id_order", "tracking_number"],
24
+ )
25
+ result = self._query("order_carriers", params, "order_carriers")
26
+ return result if result is not None else []
27
+
28
+ def get_latest_by_tracking(self, tracking: str) -> OrderCarrierData | None:
29
+ """Get the most recent order carrier by tracking number."""
30
+ carriers = self.get_by_tracking(tracking)
31
+ if not carriers:
32
+ return None
33
+ return max(carriers, key=lambda r: r.id)
@@ -0,0 +1,28 @@
1
+ from prestashop_webservice.base_model import BaseModel
2
+ from prestashop_webservice.models import OrderDetailData
3
+ from prestashop_webservice.params import Params
4
+
5
+
6
+ class OrderDetail(BaseModel):
7
+ """Complex queries for order_details endpoint."""
8
+
9
+ _data_class = OrderDetailData
10
+
11
+ def get_by_id(self, order_detail_id: str) -> OrderDetailData:
12
+ """Get order detail by ID."""
13
+ return self._query(f"order_details/{order_detail_id}", None, "order_detail")
14
+
15
+ def get_by_order(self, order_id: str) -> list[OrderDetailData]:
16
+ """Get details (products) for a specific order."""
17
+ params = Params(filter={"id_order": order_id}, display=["full"])
18
+ return self._query("order_details", params, "order_details")
19
+
20
+ def get_by_orders(self, order_ids: list[str]) -> list[OrderDetailData]:
21
+ """Get details for multiple orders."""
22
+ if not order_ids:
23
+ return []
24
+
25
+ orders_filter_value = "|".join(order_ids)
26
+
27
+ params = Params(filter={"id_order": f"[{orders_filter_value}]"}, display=["full"])
28
+ return self._query("order_details", params, "order_details")
@@ -0,0 +1,57 @@
1
+ from prestashop_webservice.base_model import BaseModel
2
+ from prestashop_webservice.logger import logger
3
+ from prestashop_webservice.models import OrderHistoryData
4
+ from prestashop_webservice.params import Params
5
+
6
+
7
+ class OrderHistory(BaseModel):
8
+ """Complex queries for order_histories endpoint."""
9
+
10
+ _data_class = OrderHistoryData
11
+
12
+ def get_by_order(self, order_id: str) -> list[OrderHistoryData]:
13
+ """Get history for a specific order."""
14
+ params = Params(
15
+ filter={"id_order": order_id},
16
+ display=["id", "id_order", "id_order_state", "date_add"],
17
+ )
18
+ return self._query("order_histories", params, "order_histories")
19
+
20
+ def get_recent_changes(self, limit: int = 20) -> list[OrderHistoryData]:
21
+ """Get recent order status changes."""
22
+ params = Params(
23
+ display=["id", "id_order", "id_order_state", "date_add"],
24
+ limit=limit,
25
+ )
26
+ return self._query("order_histories", params, "order_histories")
27
+
28
+ def get_by_status(self, status_id: str, limit: int = 100) -> list[OrderHistoryData]:
29
+ """Get order histories by status."""
30
+ params = Params(
31
+ filter={"id_order_state": status_id},
32
+ display=["id", "id_order", "id_order_state"],
33
+ limit=limit,
34
+ )
35
+ return self._query("order_histories", params, "order_histories")
36
+
37
+ def create(self, order_id: str, order_state_id: str, date_add: str | None = None) -> bool:
38
+ xml_payload = f"""<?xml version="1.0" encoding="UTF-8"?>
39
+ <prestashop xmlns:xlink="http://www.w3.org/1999/xlink">
40
+ <order_history>
41
+ <id_order>{order_id}</id_order>
42
+ <id_order_state>{order_state_id}</id_order_state>"""
43
+ if date_add:
44
+ xml_payload += f"\n <date_add>{date_add}</date_add>"
45
+ xml_payload += """
46
+ </order_history>
47
+ </prestashop>"""
48
+
49
+ logger.debug(f"Creating order history for order {order_id} with state {order_state_id}")
50
+
51
+ try:
52
+ self.client.post("order_histories", xml_payload)
53
+ logger.info(f"Order {order_id} status updated to {order_state_id}")
54
+ return True
55
+ except Exception as e:
56
+ logger.error(f"Failed to update order {order_id}: {e}")
57
+ return False
@@ -0,0 +1,12 @@
1
+ from prestashop_webservice.base_model import BaseModel
2
+ from prestashop_webservice.models import OrderStateData
3
+
4
+
5
+ class OrderState(BaseModel):
6
+ """Complex queries for order_states endpoint."""
7
+
8
+ _data_class = OrderStateData
9
+
10
+ def get_by_id(self, state_id: str) -> OrderStateData:
11
+ """Get order state by ID."""
12
+ return self._query(f"order_states/{state_id}", None, "order_state")