producteca 1.0.14__py3-none-any.whl → 2.0.14__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.
- producteca/abstract/abstract_dataclass.py +9 -21
- producteca/client.py +7 -22
- producteca/config/__init__.py +0 -1
- producteca/payments/payments.py +1 -13
- producteca/payments/tests/test_payments.py +5 -5
- producteca/products/__init__.py +0 -1
- producteca/products/products.py +88 -63
- producteca/{search/search.py → products/search_products.py} +1 -11
- producteca/products/tests/test_products.py +25 -27
- producteca/{search/tests/test_search.py → products/tests/test_search_products.py} +5 -76
- producteca/sales_orders/__init__.py +0 -1
- producteca/sales_orders/sales_orders.py +135 -42
- producteca/sales_orders/search_sale_orders.py +152 -0
- producteca/sales_orders/tests/search.json +137 -0
- producteca/sales_orders/tests/test_sales_orders.py +17 -18
- producteca/sales_orders/tests/test_search_so.py +48 -0
- producteca/shipments/shipment.py +0 -14
- producteca/shipments/tests/test_shipment.py +10 -17
- {producteca-1.0.14.dist-info → producteca-2.0.14.dist-info}/METADATA +31 -1
- producteca-2.0.14.dist-info/RECORD +31 -0
- producteca/search/__init__.py +0 -0
- producteca/search/search_sale_orders.py +0 -170
- producteca/search/tests/__init__.py +0 -0
- producteca-1.0.14.dist-info/RECORD +0 -31
- {producteca-1.0.14.dist-info → producteca-2.0.14.dist-info}/WHEEL +0 -0
- {producteca-1.0.14.dist-info → producteca-2.0.14.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,15 @@
|
|
1
1
|
from pydantic import BaseModel, Field
|
2
2
|
from typing import List, Optional
|
3
3
|
import requests
|
4
|
-
from
|
4
|
+
from producteca.abstract.abstract_dataclass import BaseService
|
5
|
+
from producteca.sales_orders.search_sale_orders import SearchSalesOrderParams, SearchSalesOrder
|
6
|
+
from producteca.payments.payments import Payment
|
7
|
+
from producteca.shipments.shipment import Shipment
|
8
|
+
from dataclasses import dataclass
|
5
9
|
import logging
|
6
10
|
_logger = logging.getLogger(__name__)
|
7
11
|
|
12
|
+
|
8
13
|
class SaleOrderLocation(BaseModel):
|
9
14
|
streetName: Optional[str] = None
|
10
15
|
streetNumber: Optional[str] = None
|
@@ -14,6 +19,7 @@ class SaleOrderLocation(BaseModel):
|
|
14
19
|
neighborhood: Optional[str] = None
|
15
20
|
zipCode: Optional[str] = None
|
16
21
|
|
22
|
+
|
17
23
|
class SaleOrderBillingInfo(BaseModel):
|
18
24
|
docType: Optional[str] = None
|
19
25
|
docNumber: Optional[str] = None
|
@@ -29,11 +35,13 @@ class SaleOrderBillingInfo(BaseModel):
|
|
29
35
|
lastName: Optional[str] = None
|
30
36
|
businessName: Optional[str] = None
|
31
37
|
|
38
|
+
|
32
39
|
class SaleOrderProfile(BaseModel):
|
33
40
|
app: int
|
34
41
|
integrationId: str
|
35
42
|
nickname: Optional[str] = None
|
36
43
|
|
44
|
+
|
37
45
|
class SaleOrderContact(BaseModel):
|
38
46
|
id: int
|
39
47
|
name: str
|
@@ -49,15 +57,18 @@ class SaleOrderContact(BaseModel):
|
|
49
57
|
profile: Optional[SaleOrderProfile] = None
|
50
58
|
billingInfo: Optional[SaleOrderBillingInfo] = None
|
51
59
|
|
60
|
+
|
52
61
|
class SaleOrderIntegrationId(BaseModel):
|
53
62
|
alternateId: Optional[str] = None
|
54
63
|
integrationId: str
|
55
64
|
app: int
|
56
65
|
|
66
|
+
|
57
67
|
class SaleOrderVariationPicture(BaseModel):
|
58
68
|
url: str
|
59
69
|
id: Optional[int] = None
|
60
70
|
|
71
|
+
|
61
72
|
class SaleOrderVariationStock(BaseModel):
|
62
73
|
warehouseId: Optional[int] = None
|
63
74
|
warehouse: str
|
@@ -66,10 +77,12 @@ class SaleOrderVariationStock(BaseModel):
|
|
66
77
|
lastModified: Optional[str] = None
|
67
78
|
available: int
|
68
79
|
|
80
|
+
|
69
81
|
class SaleOrderVariationAttribute(BaseModel):
|
70
82
|
key: str
|
71
83
|
value: str
|
72
84
|
|
85
|
+
|
73
86
|
class SaleOrderVariation(BaseModel):
|
74
87
|
supplierCode: Optional[str] = None
|
75
88
|
pictures: Optional[List[SaleOrderVariationPicture]] = None
|
@@ -86,15 +99,18 @@ class SaleOrderVariation(BaseModel):
|
|
86
99
|
sku: str
|
87
100
|
barcode: Optional[str] = None
|
88
101
|
|
102
|
+
|
89
103
|
class SaleOrderProduct(BaseModel):
|
90
104
|
name: str
|
91
105
|
code: str
|
92
106
|
brand: Optional[str] = None
|
93
107
|
id: int
|
94
108
|
|
109
|
+
|
95
110
|
class SaleOrderConversation(BaseModel):
|
96
111
|
questions: Optional[List[str]] = None
|
97
112
|
|
113
|
+
|
98
114
|
class SaleOrderLine(BaseModel):
|
99
115
|
price: float
|
100
116
|
originalPrice: Optional[float] = None
|
@@ -107,6 +123,7 @@ class SaleOrderLine(BaseModel):
|
|
107
123
|
reserved: Optional[int] = None
|
108
124
|
id: int
|
109
125
|
|
126
|
+
|
110
127
|
class SaleOrderCard(BaseModel):
|
111
128
|
paymentNetwork: Optional[str] = None
|
112
129
|
firstSixDigits: Optional[int] = None
|
@@ -115,10 +132,12 @@ class SaleOrderCard(BaseModel):
|
|
115
132
|
cardholderIdentificationType: Optional[str] = None
|
116
133
|
cardholderName: Optional[str] = None
|
117
134
|
|
135
|
+
|
118
136
|
class SaleOrderPaymentIntegration(BaseModel):
|
119
137
|
integrationId: str
|
120
138
|
app: int
|
121
139
|
|
140
|
+
|
122
141
|
class SaleOrderPayment(BaseModel):
|
123
142
|
date: Optional[str] = None
|
124
143
|
amount: float
|
@@ -134,6 +153,7 @@ class SaleOrderPayment(BaseModel):
|
|
134
153
|
hasCancelableStatus: Optional[bool] = None
|
135
154
|
id: Optional[int] = None
|
136
155
|
|
156
|
+
|
137
157
|
class SaleOrderShipmentMethod(BaseModel):
|
138
158
|
trackingNumber: Optional[str] = None
|
139
159
|
trackingUrl: Optional[str] = None
|
@@ -144,17 +164,20 @@ class SaleOrderShipmentMethod(BaseModel):
|
|
144
164
|
eta: Optional[int] = None
|
145
165
|
status: Optional[str] = None
|
146
166
|
|
167
|
+
|
147
168
|
class SaleOrderShipmentProduct(BaseModel):
|
148
169
|
product: int
|
149
170
|
variation: int
|
150
171
|
quantity: int
|
151
172
|
|
173
|
+
|
152
174
|
class SaleOrderShipmentIntegration(BaseModel):
|
153
175
|
app: int
|
154
176
|
integrationId: str
|
155
177
|
status: str
|
156
178
|
id: int
|
157
179
|
|
180
|
+
|
158
181
|
class SaleOrderShipment(BaseModel):
|
159
182
|
date: str
|
160
183
|
products: List[SaleOrderShipmentProduct]
|
@@ -163,6 +186,7 @@ class SaleOrderShipment(BaseModel):
|
|
163
186
|
receiver: Optional[dict] = None
|
164
187
|
id: int
|
165
188
|
|
189
|
+
|
166
190
|
class SaleOrderInvoiceIntegration(BaseModel):
|
167
191
|
id: Optional[int] = None
|
168
192
|
integrationId: Optional[str] = None
|
@@ -172,6 +196,7 @@ class SaleOrderInvoiceIntegration(BaseModel):
|
|
172
196
|
xmlUrl: Optional[str] = None
|
173
197
|
decreaseStock: Optional[bool] = None
|
174
198
|
|
199
|
+
|
175
200
|
class SaleOrder(BaseModel):
|
176
201
|
tags: Optional[List[str]] = None
|
177
202
|
integrations: Optional[List[SaleOrderIntegrationId]] = None
|
@@ -210,46 +235,114 @@ class SaleOrder(BaseModel):
|
|
210
235
|
notes: Optional[str] = None
|
211
236
|
id: int
|
212
237
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
238
|
+
|
239
|
+
@dataclass
|
240
|
+
class SaleOrderService(BaseService[SaleOrder]):
|
241
|
+
endpoint: str = Field(default='salesorders', exclude=True)
|
242
|
+
|
243
|
+
def __call__(self, **payload):
|
244
|
+
self._record = SaleOrder(**payload)
|
245
|
+
return self
|
246
|
+
|
247
|
+
def get(self, sale_order_id: int) -> "SaleOrder":
|
248
|
+
endpoint = f'{self.endpoint}/{sale_order_id}'
|
249
|
+
url = self.config.get_endpoint(endpoint)
|
250
|
+
response = requests.get(url, headers=self.config.headers)
|
251
|
+
if not response.ok:
|
252
|
+
raise Exception("Order could not be fetched")
|
253
|
+
return SaleOrder(**response.json())
|
254
|
+
|
255
|
+
def get_shipping_labels(self):
|
256
|
+
if not self._record:
|
257
|
+
raise Exception("You need to add a record id")
|
258
|
+
endpoint = f'{self.endpoint}/{self._record.id}/labels'
|
259
|
+
url = self.config.get_endpoint(endpoint)
|
260
|
+
response = requests.get(url, headers=self.config.headers)
|
261
|
+
if not response.ok:
|
262
|
+
raise Exception("labels could not be gotten")
|
225
263
|
return response.json()
|
226
264
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
265
|
+
def close(self):
|
266
|
+
if not self._record:
|
267
|
+
raise Exception("You need to add a record id")
|
268
|
+
endpoint = f'{self.endpoint}/{self._record.id}/close'
|
269
|
+
url = self.config.get_endpoint(endpoint)
|
270
|
+
response = requests.post(url, headers=self.config.headers)
|
271
|
+
if not response.ok:
|
272
|
+
raise Exception("Order could not be closed")
|
273
|
+
|
274
|
+
def cancel(self):
|
275
|
+
if not self._record:
|
276
|
+
raise Exception("You need to add a record id")
|
277
|
+
endpoint = f'{self.endpoint}/{self._record.id}/cancel'
|
278
|
+
url = self.config.get_endpoint(endpoint)
|
279
|
+
response = requests.post(url, headers=self.config.headers)
|
280
|
+
if not response.ok:
|
281
|
+
raise Exception("Order could not be closed")
|
282
|
+
|
283
|
+
def synchronize(self, payload: "SaleOrder") -> "SaleOrder":
|
284
|
+
endpoint = f'{self.endpoint}/synchronize'
|
285
|
+
url = self.config.get_endpoint(endpoint)
|
286
|
+
response = requests.post(url, data=payload.model_dump_json(exclude_none=True), headers=self.config.headers)
|
287
|
+
if not response.ok:
|
288
|
+
raise Exception(f"Synchronize error {response.text}")
|
289
|
+
return SaleOrder(**response.json())
|
290
|
+
|
291
|
+
def invoice_integration(self):
|
292
|
+
if not self._record:
|
293
|
+
raise Exception("You need to add a record id")
|
294
|
+
endpoint = f'{self.endpoint}/{self._record.id}/invoiceIntegration'
|
295
|
+
url = self.config.get_endpoint(endpoint)
|
296
|
+
response = requests.put(url, headers=self.config.headers, data=self._record.model_dump_json(exclude_none=True))
|
297
|
+
if not response.ok:
|
298
|
+
raise Exception(f"Error on resposne {response.text}")
|
299
|
+
return SaleOrder(**response.json())
|
300
|
+
|
301
|
+
def search(self, params: SearchSalesOrderParams):
|
302
|
+
endpoint: str = f"search/{self.endpoint}"
|
303
|
+
headers = self.config.headers
|
304
|
+
url = self.config.get_endpoint(endpoint)
|
305
|
+
new_url = f"{url}?$filter={params.filter}&top={params.top}&skip={params.skip}"
|
306
|
+
response = requests.get(
|
307
|
+
new_url,
|
308
|
+
headers=headers,
|
309
|
+
)
|
310
|
+
if not response.ok:
|
311
|
+
raise Exception(f"Error on resposne {response.text}")
|
312
|
+
return SearchSalesOrder(**response.json())
|
313
|
+
|
314
|
+
def add_payment(self, payload: "Payment") -> "Payment":
|
315
|
+
if not self._record:
|
316
|
+
raise Exception("You need to add a record id")
|
317
|
+
url = self.config.get_endpoint(f"{self.endpoint}/{self._record.id}/payments")
|
318
|
+
res = requests.post(url, data=payload.model_dump_json(exclude_none=True), headers=self.config.headers)
|
319
|
+
if not res.ok:
|
320
|
+
raise Exception(f"Error on resposne {res.text}")
|
321
|
+
return Payment(**res.json())
|
322
|
+
|
323
|
+
def update_payment(self, payment_id: int, payload: "Payment") -> "Payment":
|
324
|
+
if not self._record:
|
325
|
+
raise Exception("You need to add a record id")
|
326
|
+
url = self.config.get_endpoint(f"{self.endpoint}/{self._record.id}/payments/{payment_id}")
|
327
|
+
res = requests.put(url, data=payload.model_dump_json(exclude_none=True), headers=self.config.headers)
|
328
|
+
if not res.ok:
|
329
|
+
raise Exception(f"Error on payment update {res.text}")
|
330
|
+
return Payment(**res.json())
|
331
|
+
|
332
|
+
def add_shipment(self, payload: "Shipment") -> "Shipment":
|
333
|
+
if not self._record:
|
334
|
+
raise Exception("You need to add a record id")
|
335
|
+
url = self.config.get_endpoint(f"{self.endpoint}/{self._record.id}/shipments")
|
336
|
+
res = requests.post(url, data=payload.model_dump_json(exclude_none=True), headers=self.config.headers)
|
337
|
+
if not res.ok:
|
338
|
+
raise Exception(f"Error on shipment add {res.text}")
|
339
|
+
return Shipment(**res.json())
|
340
|
+
|
341
|
+
def update_shipment(self, shipment_id: str, payload: "Shipment") -> "Shipment":
|
342
|
+
if not self._record:
|
343
|
+
raise Exception("You need to add a record id")
|
344
|
+
url = self.config.get_endpoint(f"{self.endpoint}/{self._record.id}/shipments/{shipment_id}")
|
345
|
+
res = requests.put(url, data=payload.model_dump_json(exclude_none=True), headers=self.config.headers)
|
346
|
+
if not res.ok:
|
347
|
+
raise Exception(f"Error on shipment update {res.text}")
|
348
|
+
return Shipment(**res.json())
|
@@ -0,0 +1,152 @@
|
|
1
|
+
from typing import List, Optional
|
2
|
+
from pydantic import BaseModel, Field
|
3
|
+
import logging
|
4
|
+
|
5
|
+
_logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
|
8
|
+
class SalesOrderProduct(BaseModel):
|
9
|
+
id: int
|
10
|
+
name: str
|
11
|
+
code: str
|
12
|
+
brand: str
|
13
|
+
|
14
|
+
|
15
|
+
class SalesOrderVariationAttribute(BaseModel):
|
16
|
+
key: str
|
17
|
+
value: str
|
18
|
+
|
19
|
+
|
20
|
+
class SalesOrderVariation(BaseModel):
|
21
|
+
id: int
|
22
|
+
attributes: List[SalesOrderVariationAttribute]
|
23
|
+
sku: str
|
24
|
+
thumbnail: str
|
25
|
+
|
26
|
+
|
27
|
+
class SalesOrderLine(BaseModel):
|
28
|
+
product: SalesOrderProduct
|
29
|
+
variation: SalesOrderVariation
|
30
|
+
quantity: int
|
31
|
+
price: float
|
32
|
+
|
33
|
+
|
34
|
+
class SalesOrderCard(BaseModel):
|
35
|
+
payment_network: str = Field(alias="paymentNetwork")
|
36
|
+
first_six_digits: int = Field(alias="firstSixDigits")
|
37
|
+
last_four_digits: int = Field(alias="lastFourDigits")
|
38
|
+
cardholder_identification_number: str = Field(alias="cardholderIdentificationNumber")
|
39
|
+
cardholder_identification_type: str = Field(alias="cardholderIdentificationType")
|
40
|
+
cardholder_name: str = Field(alias="cardholderName")
|
41
|
+
|
42
|
+
|
43
|
+
class SalesOrderPaymentIntegration(BaseModel):
|
44
|
+
integration_id: str = Field(alias="integrationId")
|
45
|
+
app: int
|
46
|
+
|
47
|
+
|
48
|
+
class SalesOrderPayment(BaseModel):
|
49
|
+
date: str
|
50
|
+
amount: float
|
51
|
+
coupon_amount: float = Field(alias="couponAmount")
|
52
|
+
status: str
|
53
|
+
method: str
|
54
|
+
integration: SalesOrderPaymentIntegration
|
55
|
+
transaction_fee: float = Field(alias="transactionFee")
|
56
|
+
installments: int
|
57
|
+
card: SalesOrderCard
|
58
|
+
notes: str
|
59
|
+
has_cancelable_status: bool = Field(alias="hasCancelableStatus")
|
60
|
+
id: int
|
61
|
+
|
62
|
+
|
63
|
+
class SalesOrderIntegration(BaseModel):
|
64
|
+
alternate_id: str = Field(alias="alternateId")
|
65
|
+
integration_id: int = Field(alias="integrationId")
|
66
|
+
app: int
|
67
|
+
|
68
|
+
|
69
|
+
class SalesOrderShipmentProduct(BaseModel):
|
70
|
+
product: int
|
71
|
+
variation: int
|
72
|
+
quantity: int
|
73
|
+
|
74
|
+
|
75
|
+
class SalesOrderShipmentMethod(BaseModel):
|
76
|
+
tracking_number: str = Field(alias="trackingNumber")
|
77
|
+
tracking_url: str = Field(alias="trackingUrl")
|
78
|
+
courier: str
|
79
|
+
mode: str
|
80
|
+
cost: float
|
81
|
+
type: str
|
82
|
+
eta: str
|
83
|
+
status: str
|
84
|
+
|
85
|
+
|
86
|
+
class SalesOrderShipmentIntegration(BaseModel):
|
87
|
+
id: int
|
88
|
+
integration_id: str = Field(alias="integrationId")
|
89
|
+
app: int
|
90
|
+
status: str
|
91
|
+
|
92
|
+
|
93
|
+
class SalesOrderShipment(BaseModel):
|
94
|
+
date: str
|
95
|
+
products: List[SalesOrderShipmentProduct]
|
96
|
+
method: SalesOrderShipmentMethod
|
97
|
+
integration: SalesOrderShipmentIntegration
|
98
|
+
|
99
|
+
|
100
|
+
class SalesOrderResultItem(BaseModel):
|
101
|
+
codes: List[str]
|
102
|
+
contact_id: int = Field(alias="contactId")
|
103
|
+
currency: str
|
104
|
+
date: str
|
105
|
+
delivery_method: str = Field(alias="deliveryMethod")
|
106
|
+
delivery_status: str = Field(alias="deliveryStatus")
|
107
|
+
id: str
|
108
|
+
integration_ids: List[str] = Field(alias="integrationIds")
|
109
|
+
integrations: List[SalesOrderIntegration]
|
110
|
+
invoice_integration_app: int = Field(alias="invoiceIntegrationApp")
|
111
|
+
invoice_integration_id: str = Field(alias="invoiceIntegrationId")
|
112
|
+
lines: List[SalesOrderLine]
|
113
|
+
payments: List[SalesOrderPayment]
|
114
|
+
payment_status: str = Field(alias="paymentStatus")
|
115
|
+
payment_term: str = Field(alias="paymentTerm")
|
116
|
+
product_names: List[str] = Field(alias="productNames")
|
117
|
+
reserving_product_ids: str = Field(alias="reservingProductIds")
|
118
|
+
sales_channel: int = Field(alias="salesChannel")
|
119
|
+
shipments: List[SalesOrderShipment]
|
120
|
+
tracking_number: str = Field(alias="trackingNumber")
|
121
|
+
skus: List[str]
|
122
|
+
status: str
|
123
|
+
tags: List[str]
|
124
|
+
warehouse: str
|
125
|
+
company_id: int = Field(alias="companyId")
|
126
|
+
shipping_cost: float = Field(alias="shippingCost")
|
127
|
+
contact_phone: str = Field(alias="contactPhone")
|
128
|
+
brands: List[str]
|
129
|
+
courier: str
|
130
|
+
order_id: int = Field(alias="orderId")
|
131
|
+
updated_at: str = Field(alias="updatedAt")
|
132
|
+
invoice_integration_created_at: str = Field(alias="invoiceIntegrationCreatedAt")
|
133
|
+
invoice_integration_document_url: str = Field(alias="invoiceIntegrationDocumentUrl")
|
134
|
+
has_document_url: bool = Field(alias="hasDocumentUrl")
|
135
|
+
integration_alternate_ids: str = Field(alias="integrationAlternateIds")
|
136
|
+
cart_id: str = Field(alias="cartId")
|
137
|
+
amount: float
|
138
|
+
has_any_shipments: bool = Field(alias="hasAnyShipments")
|
139
|
+
|
140
|
+
|
141
|
+
class SearchSalesOrder(BaseModel):
|
142
|
+
count: int
|
143
|
+
results: List[SalesOrderResultItem]
|
144
|
+
|
145
|
+
|
146
|
+
class SearchSalesOrderParams(BaseModel):
|
147
|
+
top: Optional[int]
|
148
|
+
skip: Optional[int]
|
149
|
+
filter: Optional[str] = Field(default=None, alias="$filter")
|
150
|
+
|
151
|
+
class Config:
|
152
|
+
validate_by_name = True
|
@@ -0,0 +1,137 @@
|
|
1
|
+
{
|
2
|
+
"count": 0,
|
3
|
+
"results": [
|
4
|
+
{
|
5
|
+
"codes": [
|
6
|
+
"string"
|
7
|
+
],
|
8
|
+
"contactId": 0,
|
9
|
+
"currency": "Local",
|
10
|
+
"date": "string",
|
11
|
+
"deliveryMethod": "ToBeConfirmed",
|
12
|
+
"deliveryStatus": "ToBeConfirmed",
|
13
|
+
"id": "string",
|
14
|
+
"integrationIds": [
|
15
|
+
"string"
|
16
|
+
],
|
17
|
+
"integrations": [
|
18
|
+
{
|
19
|
+
"alternateId": "string",
|
20
|
+
"integrationId": 0,
|
21
|
+
"app": 0
|
22
|
+
}
|
23
|
+
],
|
24
|
+
"invoiceIntegrationApp": 0,
|
25
|
+
"invoiceIntegrationId": "string",
|
26
|
+
"lines": [
|
27
|
+
{
|
28
|
+
"product": {
|
29
|
+
"id": 0,
|
30
|
+
"name": "string",
|
31
|
+
"code": "string",
|
32
|
+
"brand": "string"
|
33
|
+
},
|
34
|
+
"variation": {
|
35
|
+
"id": 0,
|
36
|
+
"attributes": [
|
37
|
+
{
|
38
|
+
"key": "string",
|
39
|
+
"value": "string"
|
40
|
+
}
|
41
|
+
],
|
42
|
+
"sku": "string",
|
43
|
+
"thumbnail": "string"
|
44
|
+
},
|
45
|
+
"quantity": 0,
|
46
|
+
"price": 0
|
47
|
+
}
|
48
|
+
],
|
49
|
+
"payments": [
|
50
|
+
{
|
51
|
+
"date": "string",
|
52
|
+
"amount": 0,
|
53
|
+
"couponAmount": 0,
|
54
|
+
"status": "Pending",
|
55
|
+
"method": "Cash",
|
56
|
+
"integration": {
|
57
|
+
"integrationId": "string",
|
58
|
+
"app": 249
|
59
|
+
},
|
60
|
+
"transactionFee": 0,
|
61
|
+
"installments": 0,
|
62
|
+
"card": {
|
63
|
+
"paymentNetwork": "string",
|
64
|
+
"firstSixDigits": 0,
|
65
|
+
"lastFourDigits": 0,
|
66
|
+
"cardholderIdentificationNumber": "string",
|
67
|
+
"cardholderIdentificationType": "string",
|
68
|
+
"cardholderName": "string"
|
69
|
+
},
|
70
|
+
"notes": "string",
|
71
|
+
"hasCancelableStatus": true,
|
72
|
+
"id": 0
|
73
|
+
}
|
74
|
+
],
|
75
|
+
"paymentStatus": "Pending",
|
76
|
+
"paymentTerm": "Advance",
|
77
|
+
"productNames": [
|
78
|
+
"string"
|
79
|
+
],
|
80
|
+
"reservingProductIds": "string",
|
81
|
+
"salesChannel": 0,
|
82
|
+
"shipments": [
|
83
|
+
{
|
84
|
+
"date": "string",
|
85
|
+
"products": [
|
86
|
+
{
|
87
|
+
"product": 0,
|
88
|
+
"variation": 0,
|
89
|
+
"quantity": 0
|
90
|
+
}
|
91
|
+
],
|
92
|
+
"method": {
|
93
|
+
"trackingNumber": "string",
|
94
|
+
"trackingUrl": "string",
|
95
|
+
"courier": "string",
|
96
|
+
"mode": "string",
|
97
|
+
"cost": 0,
|
98
|
+
"type": "Ship",
|
99
|
+
"eta": "string",
|
100
|
+
"status": "PickingPending"
|
101
|
+
},
|
102
|
+
"integration": {
|
103
|
+
"id": 0,
|
104
|
+
"integrationId": "string",
|
105
|
+
"app": 0,
|
106
|
+
"status": "NotAvailable"
|
107
|
+
}
|
108
|
+
}
|
109
|
+
],
|
110
|
+
"trackingNumber": "string",
|
111
|
+
"skus": [
|
112
|
+
"string"
|
113
|
+
],
|
114
|
+
"status": "Pending",
|
115
|
+
"tags": [
|
116
|
+
"string"
|
117
|
+
],
|
118
|
+
"warehouse": "string",
|
119
|
+
"companyId": 0,
|
120
|
+
"shippingCost": 0,
|
121
|
+
"contactPhone": "string",
|
122
|
+
"brands": [
|
123
|
+
"string"
|
124
|
+
],
|
125
|
+
"courier": "string",
|
126
|
+
"orderId": 0,
|
127
|
+
"updatedAt": "string",
|
128
|
+
"invoiceIntegrationCreatedAt": "string",
|
129
|
+
"invoiceIntegrationDocumentUrl": "string",
|
130
|
+
"hasDocumentUrl": true,
|
131
|
+
"integrationAlternateIds": "string",
|
132
|
+
"cartId": "string",
|
133
|
+
"amount": 0,
|
134
|
+
"hasAnyShipments": true
|
135
|
+
}
|
136
|
+
]
|
137
|
+
}
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import unittest
|
2
2
|
from unittest.mock import patch, Mock
|
3
|
-
from producteca.config.config import ConfigProducteca
|
4
3
|
from producteca.sales_orders.sales_orders import SaleOrder, SaleOrderInvoiceIntegration
|
4
|
+
from producteca.client import ProductecaClient
|
5
5
|
|
6
6
|
|
7
7
|
class TestSaleOrder(unittest.TestCase):
|
8
|
+
|
8
9
|
def setUp(self):
|
9
|
-
self.
|
10
|
+
self.client = ProductecaClient(token="test_client", api_key="test_secret")
|
10
11
|
self.sale_order_id = 123
|
11
12
|
self.mock_response = {
|
12
13
|
"id": self.sale_order_id,
|
@@ -21,7 +22,7 @@ class TestSaleOrder(unittest.TestCase):
|
|
21
22
|
json=lambda: self.mock_response
|
22
23
|
)
|
23
24
|
|
24
|
-
sale_order =
|
25
|
+
sale_order = self.client.SalesOrder.get(self.sale_order_id)
|
25
26
|
self.assertEqual(sale_order.id, self.sale_order_id)
|
26
27
|
mock_get.assert_called_once()
|
27
28
|
|
@@ -33,20 +34,17 @@ class TestSaleOrder(unittest.TestCase):
|
|
33
34
|
json=lambda: mock_labels
|
34
35
|
)
|
35
36
|
|
36
|
-
labels =
|
37
|
+
labels = self.client.SalesOrder(id=1234).get_shipping_labels()
|
37
38
|
self.assertEqual(labels, mock_labels)
|
38
39
|
mock_get.assert_called_once()
|
39
40
|
|
40
41
|
@patch('requests.post')
|
41
42
|
def test_close_sale_order(self, mock_post):
|
42
43
|
mock_post.return_value = Mock(
|
43
|
-
status_code=200
|
44
|
-
json=lambda: {"status": "closed"}
|
44
|
+
status_code=200
|
45
45
|
)
|
46
46
|
|
47
|
-
|
48
|
-
self.assertEqual(status_code, 200)
|
49
|
-
self.assertEqual(response, {"status": "closed"})
|
47
|
+
self.client.SalesOrder(id=1234).close()
|
50
48
|
mock_post.assert_called_once()
|
51
49
|
|
52
50
|
@patch('requests.post')
|
@@ -56,9 +54,7 @@ class TestSaleOrder(unittest.TestCase):
|
|
56
54
|
json=lambda: {"status": "cancelled"}
|
57
55
|
)
|
58
56
|
|
59
|
-
|
60
|
-
self.assertEqual(status_code, 200)
|
61
|
-
self.assertEqual(response, {"status": "cancelled"})
|
57
|
+
self.client.SalesOrder(id=1234).cancel()
|
62
58
|
mock_post.assert_called_once()
|
63
59
|
|
64
60
|
@patch('requests.post')
|
@@ -69,8 +65,7 @@ class TestSaleOrder(unittest.TestCase):
|
|
69
65
|
json=lambda: self.mock_response
|
70
66
|
)
|
71
67
|
|
72
|
-
|
73
|
-
self.assertEqual(status_code, 200)
|
68
|
+
response = self.client.SalesOrder.synchronize(sale_order)
|
74
69
|
self.assertEqual(response.id, self.sale_order_id)
|
75
70
|
mock_post.assert_called_once()
|
76
71
|
|
@@ -86,13 +81,17 @@ class TestSaleOrder(unittest.TestCase):
|
|
86
81
|
|
87
82
|
mock_put.return_value = Mock(
|
88
83
|
status_code=200,
|
89
|
-
json=lambda: {
|
84
|
+
json=lambda: {
|
85
|
+
"id": 1,
|
86
|
+
"integrationId": "test123",
|
87
|
+
"app": 1
|
88
|
+
}
|
90
89
|
)
|
91
90
|
|
92
|
-
|
93
|
-
self.
|
94
|
-
self.assertEqual(response, {})
|
91
|
+
response = self.client.SalesOrder(**sale_order.model_dump()).invoice_integration()
|
92
|
+
self.assertIsInstance(response, SaleOrder)
|
95
93
|
mock_put.assert_called_once()
|
96
94
|
|
95
|
+
|
97
96
|
if __name__ == '__main__':
|
98
97
|
unittest.main()
|