producteca 1.0.14__py3-none-any.whl → 2.0.57__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/__init__.py +3 -0
- producteca/abstract/abstract_dataclass.py +20 -18
- producteca/client.py +8 -21
- producteca/config/__init__.py +0 -1
- producteca/payments/payments.py +4 -16
- producteca/payments/tests/test_payments.py +21 -5
- producteca/products/__init__.py +0 -1
- producteca/products/products.py +237 -81
- producteca/products/search_products.py +136 -0
- producteca/products/tests/test_products.py +111 -38
- producteca/{search/tests/test_search.py → products/tests/test_search_products.py} +15 -86
- producteca/sales_orders/__init__.py +0 -1
- producteca/sales_orders/sales_orders.py +292 -118
- 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 +55 -26
- producteca/sales_orders/tests/test_search_so.py +46 -0
- producteca/shipments/shipment.py +0 -14
- producteca/shipments/tests/test_shipment.py +28 -23
- producteca/utils.py +50 -0
- producteca-2.0.57.dist-info/LICENSE +661 -0
- {producteca-1.0.14.dist-info → producteca-2.0.57.dist-info}/METADATA +33 -2
- producteca-2.0.57.dist-info/RECORD +33 -0
- producteca/search/__init__.py +0 -0
- producteca/search/search.py +0 -149
- 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.57.dist-info}/WHEEL +0 -0
- {producteca-1.0.14.dist-info → producteca-2.0.57.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class FacetValue(BaseModel):
|
|
6
|
+
count: int
|
|
7
|
+
value: Optional[Union[str, bool]] = None
|
|
8
|
+
label: Union[str, bool]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Facet(BaseModel):
|
|
12
|
+
key: str
|
|
13
|
+
value: List[FacetValue]
|
|
14
|
+
is_collection: Optional[bool] = False
|
|
15
|
+
translate: bool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SearchStocks(BaseModel):
|
|
19
|
+
warehouse: str
|
|
20
|
+
quantity: int
|
|
21
|
+
reserved: int
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SearchPrices(BaseModel):
|
|
25
|
+
price_list_id: int = Field(..., alias='priceListId')
|
|
26
|
+
price_list: str = Field(..., alias='priceList')
|
|
27
|
+
amount: float
|
|
28
|
+
currency: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SearchIntegration(BaseModel):
|
|
32
|
+
app: Optional[int] = None
|
|
33
|
+
integration_id: Optional[str] = Field(None, alias='integrationId')
|
|
34
|
+
permalink: Optional[str] = None
|
|
35
|
+
status: Optional[str] = None
|
|
36
|
+
listing_type: Optional[str] = Field(None, alias='listingType')
|
|
37
|
+
safety_stock: Optional[int] = Field(None, alias='safetyStock')
|
|
38
|
+
synchronize_stock: Optional[bool] = Field(None, alias='synchronizeStock')
|
|
39
|
+
is_active: Optional[bool] = Field(None, alias='isActive')
|
|
40
|
+
is_active_or_paused: Optional[bool] = Field(None, alias='isActiveOrPaused')
|
|
41
|
+
id: Optional[int] = None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class SearchDeals(BaseModel):
|
|
45
|
+
campaign: str
|
|
46
|
+
product: int
|
|
47
|
+
variation: str
|
|
48
|
+
deal_price: float
|
|
49
|
+
discount: float
|
|
50
|
+
regular_price: float
|
|
51
|
+
enabled: bool
|
|
52
|
+
currency: str
|
|
53
|
+
id: str
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SearchResultItem(BaseModel):
|
|
57
|
+
search_score: float = Field(..., alias='@search.score')
|
|
58
|
+
id: int
|
|
59
|
+
product_id: Optional[int] = Field(None, alias='productId')
|
|
60
|
+
company_id: Optional[int] = Field(None, alias='companyId')
|
|
61
|
+
name:Optional[str] = None
|
|
62
|
+
code: Optional[str] = None
|
|
63
|
+
skus: List[str]
|
|
64
|
+
brand: Optional[str] = None
|
|
65
|
+
category: Optional[str] = None
|
|
66
|
+
thumbnail: Optional[str] = None
|
|
67
|
+
stocks: Optional[Union[List[SearchStocks], List]] = None
|
|
68
|
+
warehouses_with_stock: Optional[List[str]] = Field(None, alias='warehousesWithStock')
|
|
69
|
+
total_stock: Optional[int] = Field(None, alias='totalStock')
|
|
70
|
+
has_pictures: Optional[bool] = Field(None, alias='hasPictures')
|
|
71
|
+
buying_price: Optional[float] = Field(None, alias='buyingPrice')
|
|
72
|
+
prices: Optional[Union[List[SearchPrices], List]] = None
|
|
73
|
+
integration_ids: Optional[List[str]] = Field(None, alias='integrationIds')
|
|
74
|
+
integration_apps: Optional[List[str]] = Field(None, alias='integrationApps')
|
|
75
|
+
integrations: Optional[Union[List[SearchIntegration], List]] = None
|
|
76
|
+
campaigns: Optional[List[str]] = None
|
|
77
|
+
app: Optional[int] = None
|
|
78
|
+
status: Optional[str] = None
|
|
79
|
+
synchronize_stock: Optional[bool] = Field(None, alias='synchronizeStock')
|
|
80
|
+
listing_type: Optional[str] = Field(None, alias='listingType')
|
|
81
|
+
price_amount: Optional[float] = Field(None, alias='priceAmount')
|
|
82
|
+
price_currency: Optional[str] = Field(None, alias='priceCurrency')
|
|
83
|
+
category_id: Optional[str] = Field(None, alias='categoryId')
|
|
84
|
+
category_base_id: Optional[str] = Field(None, alias='categoryBaseId')
|
|
85
|
+
category_l1: Optional[str] = Field(None, alias='categoryL1')
|
|
86
|
+
category_l2: Optional[str] = Field(None, alias='categoryL2')
|
|
87
|
+
category_l3: Optional[str] = Field(None, alias='categoryL3')
|
|
88
|
+
category_l4: Optional[str] = Field(None, alias='categoryL4')
|
|
89
|
+
category_l5: Optional[str] = Field(None, alias='categoryL5')
|
|
90
|
+
category_l6: Optional[str] = Field(None, alias='categoryL6')
|
|
91
|
+
has_category: Optional[bool] = Field(None, alias='hasCategory')
|
|
92
|
+
category_fixed: Optional[bool] = Field(None, alias='categoryFixed')
|
|
93
|
+
accepts_mercadoenvios: Optional[bool] = Field(None, alias='acceptsMercadoenvios')
|
|
94
|
+
shipping_mode: Optional[str] = Field(None, alias='shippingMode')
|
|
95
|
+
local_pickup: Optional[bool] = Field(None, alias='localPickup')
|
|
96
|
+
mandatory_free_shipping: Optional[bool] = Field(None, alias='mandatoryFreeShipping')
|
|
97
|
+
free_shipping: Optional[bool] = Field(None, alias='freeShipping')
|
|
98
|
+
free_shipping_cost: Optional[float] = Field(None, alias='freeShippingCost')
|
|
99
|
+
template: Optional[int] = None
|
|
100
|
+
youtube_id: Optional[str] = Field(None, alias='youtubeId')
|
|
101
|
+
warranty: Optional[str] = None
|
|
102
|
+
permalink: Optional[str] = None
|
|
103
|
+
domain: Optional[str] = None
|
|
104
|
+
attribute_completion_status: Optional[str] = Field(None, alias='attributeCompletionStatus')
|
|
105
|
+
attribute_completion_count: Optional[int] = Field(None, alias='attributeCompletionCount')
|
|
106
|
+
attribute_completion_total: Optional[int] = Field(None, alias='attributeCompletionTotal')
|
|
107
|
+
deals: Optional[Union[SearchDeals, List]] = None
|
|
108
|
+
campaign_status: Optional[List[str]] = Field(None, alias='campaignStatus')
|
|
109
|
+
size_chart: Optional[str] = Field(None, alias='sizeChart')
|
|
110
|
+
channel_status: Optional[List[str]] = Field(None, alias='channelStatus')
|
|
111
|
+
channel_category_l1: Optional[List[str]] = Field(None, alias='channelCategoryL1')
|
|
112
|
+
channel_category_l2: Optional[List[str]] = Field(None, alias='channelCategoryL2')
|
|
113
|
+
channel_category_l3: Optional[List[str]] = Field(None, alias='channelCategoryL3')
|
|
114
|
+
channel_category_id: Optional[List[str]] = Field(None, alias='channelCategoryId')
|
|
115
|
+
channel_synchronizes_stock: Optional[List[str]] = Field(None, alias='channelSynchronizesStock')
|
|
116
|
+
channel_has_category: Optional[List[str]] = Field(None, alias='channelHasCategory')
|
|
117
|
+
catalog_products_status: Optional[List[str]] = Field(None, alias='catalogProductsStatus')
|
|
118
|
+
metadata: Optional[List[str]] = None
|
|
119
|
+
integration_tags: Optional[List[str]] = Field(None, alias='integrationTags')
|
|
120
|
+
variations_integration_ids: Optional[List[str]] = Field(None, alias='variationsIntegrationIds')
|
|
121
|
+
channel_pictures_templates: Optional[List[str]] = Field(None, alias='channelPicturesTemplates')
|
|
122
|
+
channel_pictures_templates_apps: Optional[List[str]] = Field(None, alias='channelPicturesTemplatesApps')
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class SearchProduct(BaseModel):
|
|
126
|
+
count: int
|
|
127
|
+
facets: List[Facet]
|
|
128
|
+
results: List[SearchResultItem]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SearchProductParams(BaseModel):
|
|
132
|
+
top: Optional[int]
|
|
133
|
+
skip: Optional[int]
|
|
134
|
+
filter: Optional[str] = Field(default=None, alias='$filter')
|
|
135
|
+
search: Optional[str] = Field(default=None)
|
|
136
|
+
sales_channel: Optional[str] = Field(default='2', alias='salesChannel')
|
|
@@ -1,95 +1,168 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
from unittest.mock import patch, Mock
|
|
3
|
-
from producteca.
|
|
4
|
-
from producteca.
|
|
3
|
+
from producteca.products.products import Product
|
|
4
|
+
from producteca.client import ProductecaClient
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class TestProduct(unittest.TestCase):
|
|
8
8
|
def setUp(self):
|
|
9
|
-
self.
|
|
9
|
+
self.client = ProductecaClient(token="test_client_id", api_key="test_client_secret")
|
|
10
10
|
self.test_product = Product(
|
|
11
|
-
config=self.config,
|
|
12
11
|
sku="TEST001",
|
|
13
12
|
name="Test Product",
|
|
14
|
-
code="TEST001"
|
|
13
|
+
code="TEST001",
|
|
14
|
+
category="Test"
|
|
15
15
|
)
|
|
16
|
+
self.product_to_create_payload = {
|
|
17
|
+
"sku": "9817234",
|
|
18
|
+
"code": "871234",
|
|
19
|
+
"name": "Hola test",
|
|
20
|
+
"buyingPrice": 0,
|
|
21
|
+
"deals": [
|
|
22
|
+
{
|
|
23
|
+
"campaign": "string",
|
|
24
|
+
"regularPrice": 0,
|
|
25
|
+
"dealPrice": 0
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"prices": [
|
|
29
|
+
{
|
|
30
|
+
"amount": 10,
|
|
31
|
+
"currency": "Local",
|
|
32
|
+
"priceList": "Default"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"stocks": [
|
|
36
|
+
{
|
|
37
|
+
"quantity": 2,
|
|
38
|
+
"availableQuantity": 2,
|
|
39
|
+
"warehouse": "Default"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
}
|
|
16
43
|
|
|
17
44
|
@patch('requests.post')
|
|
18
45
|
def test_create_product_success(self, mock_post):
|
|
19
46
|
# Mock successful response
|
|
20
47
|
mock_response = Mock()
|
|
21
48
|
mock_response.status_code = 200
|
|
22
|
-
mock_response.json.return_value =
|
|
49
|
+
mock_response.json.return_value = self.test_product.model_dump()
|
|
23
50
|
mock_post.return_value = mock_response
|
|
24
51
|
|
|
25
|
-
response
|
|
52
|
+
response = self.client.Product(**self.test_product.model_dump()).synchronize(self.product_to_create_payload)
|
|
26
53
|
|
|
27
|
-
self.assertEqual(
|
|
28
|
-
self.assertEqual(response["sku"], "TEST001")
|
|
29
|
-
|
|
30
|
-
@patch('requests.post')
|
|
31
|
-
def test_create_product_not_exist(self, mock_post):
|
|
32
|
-
# Mock product not found response
|
|
33
|
-
mock_response = Mock()
|
|
34
|
-
mock_response.status_code = 204
|
|
35
|
-
mock_post.return_value = mock_response
|
|
36
|
-
|
|
37
|
-
response, status_code = self.test_product.create()
|
|
38
|
-
|
|
39
|
-
self.assertEqual(status_code, 204)
|
|
40
|
-
self.assertEqual(response["Message"], "Product does not exist and the request cant create if it does not exist")
|
|
54
|
+
self.assertEqual(response.sku, "TEST001")
|
|
41
55
|
|
|
42
56
|
@patch('requests.post')
|
|
43
57
|
def test_update_product_success(self, mock_post):
|
|
44
|
-
|
|
58
|
+
payload = self.product_to_create_payload
|
|
45
59
|
mock_response = Mock()
|
|
46
60
|
mock_response.status_code = 200
|
|
47
|
-
mock_response.json.return_value =
|
|
61
|
+
mock_response.json.return_value = self.test_product.model_dump()
|
|
48
62
|
mock_post.return_value = mock_response
|
|
49
63
|
|
|
50
|
-
response
|
|
64
|
+
response = self.client.Product(**self.test_product.model_dump()).synchronize(payload)
|
|
51
65
|
|
|
52
|
-
self.assertEqual(
|
|
53
|
-
self.assertEqual(response["name"], "Updated Product")
|
|
66
|
+
self.assertEqual(response.name, "Test Product")
|
|
54
67
|
|
|
55
68
|
@patch('requests.get')
|
|
56
69
|
def test_get_product(self, mock_get):
|
|
57
70
|
# Mock get product response
|
|
58
71
|
mock_response = Mock()
|
|
59
72
|
mock_response.status_code = 200
|
|
60
|
-
mock_response.json.return_value =
|
|
73
|
+
mock_response.json.return_value = self.test_product.model_dump()
|
|
61
74
|
mock_get.return_value = mock_response
|
|
62
75
|
|
|
63
|
-
response
|
|
76
|
+
response = self.client.Product.get(1)
|
|
64
77
|
|
|
65
|
-
self.assertEqual(
|
|
66
|
-
self.assertEqual(response["sku"], "TEST001")
|
|
78
|
+
self.assertEqual(response.sku, "TEST001")
|
|
67
79
|
|
|
68
80
|
@patch('requests.get')
|
|
69
81
|
def test_get_bundle(self, mock_get):
|
|
70
82
|
# Mock get bundle response
|
|
71
83
|
mock_response = Mock()
|
|
72
84
|
mock_response.status_code = 200
|
|
73
|
-
|
|
85
|
+
test_prod = {
|
|
86
|
+
"results": [
|
|
87
|
+
{
|
|
88
|
+
"companyId": 0,
|
|
89
|
+
"productId": 0,
|
|
90
|
+
"variations": [
|
|
91
|
+
{
|
|
92
|
+
"variationId": 0,
|
|
93
|
+
"components": [
|
|
94
|
+
{
|
|
95
|
+
"quantity": 0,
|
|
96
|
+
"variationId": 0,
|
|
97
|
+
"productId": 0
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
],
|
|
102
|
+
"id": "string"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"count": 0
|
|
106
|
+
}
|
|
107
|
+
mock_response.json.return_value = test_prod
|
|
74
108
|
mock_get.return_value = mock_response
|
|
75
109
|
|
|
76
|
-
product
|
|
110
|
+
product = self.client.Product.get_bundle(1)
|
|
77
111
|
|
|
78
|
-
self.assertEqual(
|
|
79
|
-
self.assertEqual(product.sku, "TEST001")
|
|
112
|
+
self.assertEqual(product.count, 0)
|
|
80
113
|
|
|
81
114
|
@patch('requests.get')
|
|
82
115
|
def test_get_ml_integration(self, mock_get):
|
|
83
116
|
# Mock ML integration response
|
|
84
117
|
mock_response = Mock()
|
|
85
118
|
mock_response.status_code = 200
|
|
86
|
-
|
|
119
|
+
meli_product = {
|
|
120
|
+
"hasCustomShippingCosts": True,
|
|
121
|
+
"productId": 0,
|
|
122
|
+
"shipping": {
|
|
123
|
+
"localPickup": True,
|
|
124
|
+
"mode": "string",
|
|
125
|
+
"freeShipping": True,
|
|
126
|
+
"freeShippingCost": 0,
|
|
127
|
+
"mandatoryFreeShipping": True,
|
|
128
|
+
"freeShippingMethod": "string"
|
|
129
|
+
},
|
|
130
|
+
"mShopsShipping": {
|
|
131
|
+
"enabled": True
|
|
132
|
+
},
|
|
133
|
+
"addFreeShippingCostToPrice": True,
|
|
134
|
+
"category": {
|
|
135
|
+
"meliId": "string",
|
|
136
|
+
"acceptsMercadoenvios": True,
|
|
137
|
+
"suggest": True,
|
|
138
|
+
"fixed": True
|
|
139
|
+
},
|
|
140
|
+
"attributeCompletion": {
|
|
141
|
+
"productIdentifierStatus": "Complete",
|
|
142
|
+
"dataSheetStatus": "Complete",
|
|
143
|
+
"status": "Complete",
|
|
144
|
+
"count": 0,
|
|
145
|
+
"total": 0
|
|
146
|
+
},
|
|
147
|
+
"catalogProducts": [
|
|
148
|
+
"string"
|
|
149
|
+
],
|
|
150
|
+
"warranty": "string",
|
|
151
|
+
"domain": "string",
|
|
152
|
+
"listingTypeId": "GoldSpecial",
|
|
153
|
+
"catalogProductsStatus": "Unlinked",
|
|
154
|
+
"tags": [
|
|
155
|
+
"string"
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
mock_response.json.return_value = meli_product
|
|
87
160
|
mock_get.return_value = mock_response
|
|
88
161
|
|
|
89
|
-
product
|
|
162
|
+
product = self.client.Product.get_ml_integration(1)
|
|
90
163
|
|
|
91
|
-
self.assertEqual(
|
|
92
|
-
|
|
164
|
+
self.assertEqual(product.listing_type_id, "GoldSpecial")
|
|
165
|
+
|
|
93
166
|
|
|
94
167
|
if __name__ == '__main__':
|
|
95
168
|
unittest.main()
|
|
@@ -1,81 +1,12 @@
|
|
|
1
1
|
import unittest
|
|
2
2
|
from unittest.mock import patch, Mock
|
|
3
|
-
from producteca.
|
|
4
|
-
from producteca.
|
|
5
|
-
from producteca.search.search import SearchProduct, SearchProductParams
|
|
6
|
-
|
|
7
|
-
class TestSearchSalesOrder(unittest.TestCase):
|
|
8
|
-
def setUp(self):
|
|
9
|
-
self.config = ConfigProducteca(
|
|
10
|
-
token="test_client_id",
|
|
11
|
-
api_key="test_client_secret",
|
|
12
|
-
)
|
|
13
|
-
self.params = SearchSalesOrderParams(
|
|
14
|
-
top=10,
|
|
15
|
-
skip=0,
|
|
16
|
-
filter="status eq 'confirmed'"
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
@patch('requests.get')
|
|
20
|
-
def test_search_saleorder_success(self, mock_get):
|
|
21
|
-
# Mock successful response
|
|
22
|
-
mock_response = Mock()
|
|
23
|
-
mock_response.json.return_value = {
|
|
24
|
-
"count": 1,
|
|
25
|
-
"results": [{
|
|
26
|
-
"id": "123",
|
|
27
|
-
"status": "confirmed",
|
|
28
|
-
"lines": [],
|
|
29
|
-
"payments": [],
|
|
30
|
-
"shipments": [],
|
|
31
|
-
"integrations": [],
|
|
32
|
-
"codes": [],
|
|
33
|
-
"integration_ids": [],
|
|
34
|
-
"product_names": [],
|
|
35
|
-
"skus": [],
|
|
36
|
-
"tags": [],
|
|
37
|
-
"brands": []
|
|
38
|
-
}]
|
|
39
|
-
}
|
|
40
|
-
mock_response.status_code = 200
|
|
41
|
-
mock_get.return_value = mock_response
|
|
42
|
-
|
|
43
|
-
response, status_code = SearchSalesOrder.search_saleorder(self.config, self.params)
|
|
44
|
-
|
|
45
|
-
# Validate response
|
|
46
|
-
self.assertEqual(status_code, 200)
|
|
47
|
-
self.assertEqual(response["count"], 1)
|
|
48
|
-
self.assertEqual(len(response["results"]), 1)
|
|
49
|
-
self.assertEqual(response["results"][0]["id"], "123")
|
|
50
|
-
|
|
51
|
-
# Verify the request was made with correct parameters
|
|
52
|
-
expected_url = f"{self.config.get_endpoint(SearchSalesOrder.endpoint)}?$filter={self.params.filter}&top={self.params.top}&skip={self.params.skip}"
|
|
53
|
-
mock_get.assert_called_once_with(
|
|
54
|
-
expected_url,
|
|
55
|
-
headers=self.config.headers
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
@patch('requests.get')
|
|
59
|
-
def test_search_saleorder_error(self, mock_get):
|
|
60
|
-
# Mock error response
|
|
61
|
-
mock_response = Mock()
|
|
62
|
-
mock_response.json.return_value = {"error": "Invalid request"}
|
|
63
|
-
mock_response.status_code = 400
|
|
64
|
-
mock_get.return_value = mock_response
|
|
65
|
-
|
|
66
|
-
response, status_code = SearchSalesOrder.search_saleorder(self.config, self.params)
|
|
67
|
-
|
|
68
|
-
# Validate error response
|
|
69
|
-
self.assertEqual(status_code, 400)
|
|
70
|
-
self.assertEqual(response["error"], "Invalid request")
|
|
3
|
+
from producteca.products.search_products import SearchProductParams
|
|
4
|
+
from producteca.client import ProductecaClient
|
|
71
5
|
|
|
72
6
|
|
|
73
7
|
class TestSearchProduct(unittest.TestCase):
|
|
74
8
|
def setUp(self):
|
|
75
|
-
self.
|
|
76
|
-
token="test_client_id",
|
|
77
|
-
api_key="test_client_secret",
|
|
78
|
-
)
|
|
9
|
+
self.client = ProductecaClient(token="test_client_id", api_key="test_client_secret")
|
|
79
10
|
self.params = SearchProductParams(
|
|
80
11
|
top=10,
|
|
81
12
|
skip=0,
|
|
@@ -103,8 +34,8 @@ class TestSearchProduct(unittest.TestCase):
|
|
|
103
34
|
"results": [{
|
|
104
35
|
"@search.score": 1.0,
|
|
105
36
|
"id": 123,
|
|
106
|
-
"
|
|
107
|
-
"
|
|
37
|
+
"productId": 456,
|
|
38
|
+
"companyId": 789,
|
|
108
39
|
"name": "Test Product",
|
|
109
40
|
"code": "TEST-001",
|
|
110
41
|
"skus": ["SKU001"],
|
|
@@ -116,18 +47,18 @@ class TestSearchProduct(unittest.TestCase):
|
|
|
116
47
|
"quantity": 10,
|
|
117
48
|
"reserved": 0
|
|
118
49
|
}],
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
50
|
+
"warehousesWithStock": ["Main"],
|
|
51
|
+
"totalStock": 10,
|
|
52
|
+
"hasPictures": True,
|
|
53
|
+
"buyingPrice": 100.0,
|
|
123
54
|
"prices": [{
|
|
124
|
-
"
|
|
125
|
-
"
|
|
55
|
+
"priceListId": 1,
|
|
56
|
+
"priceList": "Default",
|
|
126
57
|
"amount": 200.0,
|
|
127
58
|
"currency": "USD"
|
|
128
59
|
}],
|
|
129
|
-
"
|
|
130
|
-
"
|
|
60
|
+
"integrationIds": ["INT001"],
|
|
61
|
+
"integrationApps": ["APP1"],
|
|
131
62
|
"integrations": [],
|
|
132
63
|
"campaigns": [],
|
|
133
64
|
"app": None,
|
|
@@ -181,15 +112,13 @@ class TestSearchProduct(unittest.TestCase):
|
|
|
181
112
|
mock_response.status_code = 200
|
|
182
113
|
mock_get.return_value = mock_response
|
|
183
114
|
|
|
184
|
-
response =
|
|
115
|
+
response = self.client.Product.search(self.params)
|
|
185
116
|
|
|
186
117
|
# Validate response
|
|
187
118
|
self.assertEqual(response.count, 1)
|
|
188
119
|
self.assertEqual(len(response.results), 1)
|
|
189
120
|
self.assertEqual(response.results[0].id, 123)
|
|
190
121
|
self.assertEqual(response.results[0].name, "Test Product")
|
|
191
|
-
|
|
192
|
-
|
|
193
122
|
|
|
194
123
|
@patch('requests.get')
|
|
195
124
|
def test_search_product_error(self, mock_get):
|
|
@@ -200,7 +129,7 @@ class TestSearchProduct(unittest.TestCase):
|
|
|
200
129
|
mock_get.return_value = mock_response
|
|
201
130
|
# TODO: Fix this
|
|
202
131
|
# with self.assertRaises(Exception):
|
|
203
|
-
#
|
|
132
|
+
# self.client.Product.search(self.params)
|
|
204
133
|
|
|
205
134
|
|
|
206
135
|
if __name__ == '__main__':
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from . import sales_orders
|