producteca 1.0.13__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.
@@ -0,0 +1,48 @@
1
+ import unittest
2
+ import json
3
+ from unittest.mock import patch, Mock
4
+ from producteca.sales_orders.search_sale_orders import SearchSalesOrderParams, SalesOrderResultItem
5
+ from producteca.client import ProductecaClient
6
+
7
+
8
+ class TestSearchSalesOrder(unittest.TestCase):
9
+
10
+ def setUp(self):
11
+ self.client = ProductecaClient(token="test_client_id", api_key="test_client_secret")
12
+ self.params = SearchSalesOrderParams(
13
+ top=10,
14
+ skip=0,
15
+ filter="status eq 'confirmed'"
16
+ )
17
+
18
+ @patch('requests.get')
19
+ def test_search_saleorder_success(self, mock_get):
20
+ # Mock successful response
21
+ mock_response = Mock()
22
+ with open('producteca/sales_orders/tests/search.json', 'r') as f:
23
+ results = json.loads(f.read())
24
+ mock_response.json.return_value = results
25
+ mock_response.status_code = 200
26
+ mock_get.return_value = mock_response
27
+
28
+ response = self.client.SalesOrder.search(self.params)
29
+
30
+ # Validate response
31
+ self.assertEqual(response.count, 0)
32
+ self.assertEqual(len(response.results), 1)
33
+ self.assertEqual(response.results[0].id, "string")
34
+ self.assertIsInstance(response.results[0], SalesOrderResultItem)
35
+
36
+ @patch('requests.get')
37
+ def test_search_saleorder_error(self, mock_get):
38
+ # Mock error response
39
+ mock_response = Mock()
40
+ mock_response.json.return_value = {"error": "Invalid request"}
41
+ mock_response.status_code = 400
42
+ mock_get.return_value = mock_response
43
+ with self.assertRaises(Exception):
44
+ self.client.SalesOrder.search(self.params)
45
+
46
+
47
+ if __name__ == '__main__':
48
+ unittest.main()
@@ -1,7 +1,5 @@
1
1
  from typing import List, Optional
2
2
  from pydantic import BaseModel
3
- import requests
4
- from ..config.config import ConfigProducteca
5
3
 
6
4
 
7
5
  class ShipmentProduct(BaseModel):
@@ -33,15 +31,3 @@ class Shipment(BaseModel):
33
31
  products: Optional[List[ShipmentProduct]] = None
34
32
  method: Optional[ShipmentMethod] = None
35
33
  integration: Optional[ShipmentIntegration] = None
36
-
37
- @classmethod
38
- def create(cls, config: ConfigProducteca, sale_order_id: int, payload: "Shipment") -> "Shipment":
39
- url = config.get_endpoint(f"salesorders/{sale_order_id}/shipments")
40
- res = requests.post(url, data=payload.model_dump_json(exclude_none=True), headers=config.headers)
41
- return res.status_code, res.json()
42
-
43
- @classmethod
44
- def update(cls, config: ConfigProducteca, sale_order_id: int, shipment_id: str, payload: "Shipment") -> "Shipment":
45
- url = config.get_endpoint(f"salesorders/{sale_order_id}/shipments/{shipment_id}")
46
- res = requests.put(url, data=payload.model_dump_json(exclude_none=True), headers=config.headers)
47
- return res.status_code, res.json()
@@ -1,15 +1,16 @@
1
1
  import unittest
2
2
  from unittest.mock import patch, MagicMock
3
- from producteca.shipments.shipment import Shipment, ShipmentProduct, ShipmentMethod, ShipmentIntegration, ConfigProducteca
3
+ from producteca.shipments.shipment import Shipment, ShipmentProduct, ShipmentMethod, ShipmentIntegration
4
+ from producteca.client import ProductecaClient
4
5
 
5
6
 
6
7
  class TestShipment(unittest.TestCase):
7
-
8
+ def setUp(self):
9
+ self.client = ProductecaClient(token="test_id", api_key="test_secret")
10
+
8
11
  @patch('requests.post')
9
12
  def test_create_shipment(self, mock_post):
10
13
  # Arrange
11
- config = ConfigProducteca(token="test_token", api_key="as")
12
- sale_order_id = 123
13
14
  products = [ShipmentProduct(product=1, variation=2, quantity=3)]
14
15
  method = ShipmentMethod(trackingNumber="TN123", trackingUrl="http://track.url", courier="DHL", mode="air", cost=10.5, type="express", eta=5, status="shipped")
15
16
  integration = ShipmentIntegration(id=1, integrationId="int123", app=10, status="active")
@@ -19,20 +20,15 @@ class TestShipment(unittest.TestCase):
19
20
  mock_response.status_code = 201
20
21
  mock_response.json.return_value = {'success': True}
21
22
  mock_post.return_value = mock_response
22
-
23
23
  # Act
24
- status_code, response_json = Shipment.create(config, sale_order_id, payload)
24
+ shipment = self.client.SalesOrder(id=1234).add_shipment(payload)
25
25
 
26
- # Assert
27
- self.assertEqual(status_code, 201)
28
- self.assertEqual(response_json, {'success': True})
26
+ self.assertIsInstance(shipment, Shipment)
29
27
  mock_post.assert_called_once()
30
28
 
31
29
  @patch('requests.put')
32
30
  def test_update_shipment(self, mock_put):
33
31
  # Arrange
34
- config = ConfigProducteca(token="test_token", api_key="as")
35
- sale_order_id = 123
36
32
  shipment_id = 'abc'
37
33
  products = [ShipmentProduct(product=4, quantity=7)]
38
34
  method = ShipmentMethod(courier="FedEx", cost=15.0)
@@ -44,14 +40,11 @@ class TestShipment(unittest.TestCase):
44
40
  mock_response.json.return_value = {'updated': True}
45
41
  mock_put.return_value = mock_response
46
42
 
47
- # Act
48
- status_code, response_json = Shipment.update(config, sale_order_id, shipment_id, payload)
43
+ shipment = self.client.SalesOrder(id=1234).update_shipment(shipment_id, payload)
49
44
 
50
- # Assert
51
- self.assertEqual(status_code, 200)
52
- self.assertEqual(response_json, {'updated': True})
45
+ self.assertIsInstance(shipment, Shipment)
53
46
  mock_put.assert_called_once()
54
47
 
55
48
 
56
49
  if __name__ == '__main__':
57
- unittest.main()
50
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: producteca
3
- Version: 1.0.13
3
+ Version: 2.0.14
4
4
  Summary:
5
5
  Author: Chroma Agency, Matias Rivera
6
6
  Author-email: mrivera@chroma.agency
@@ -38,10 +38,40 @@ To use the Producteca client, first instantiate `ProductecaClient` with your cre
38
38
  from producteca.client import ProductecaClient
39
39
 
40
40
  client = ProductecaClient(token='your_token', api_key='your_api_key')
41
+
42
+ # Usage examples for SalesOrderService
43
+ sale_order_service = client.SalesOrder
44
+ # Get from api
45
+ sale_order = sale_order_service.get(123)
46
+ # or construct from db
47
+ sale_order = client.SalesOrder(...argswithid)
48
+ labels = sale_order.get_shipping_labels()
49
+ status_code, close_response = sale_order.close()
50
+ status_code, cancel_response = sale_order.cancel()
51
+ status_code, synchronized_order = sale_order.synchronize()
52
+ status_code, invoice_response = sale_order.invoice_integration(sale_order)
53
+ search_results = sale_order_service.search(params)
54
+ payment = sale_order.add_payment(payment_payload)
55
+ updated_payment = sale_order.update_payment(payment_id, payment_payload)
56
+ shipment = sale_order.add_shipment(shipment_payload)
57
+ updated_shipment = sale_order.update_shipment(shipment_id, shipment_payload)
58
+
59
+ # Usage examples for ProductService
60
+ product_service = client.Products
61
+ # Get from api
62
+ product = product_service.get(456)
63
+ bundle = product_service.get_bundle(456)
64
+ ml_integration = product_service.get_ml_integration(456)
65
+ search_results = product_service.search(search_params)
66
+ # Or construct from db
67
+ product = client.Products(...args)
68
+ created_product = product.create()
69
+ updated_product = product.update()
41
70
  ```
42
71
 
43
72
  then with the client you should use every other module, like SalesOrder, Products, Payments, Shipments and Search
44
73
 
74
+
45
75
  ## Contributing
46
76
 
47
77
  We welcome contributions to improve this project! Here's how you can help:
@@ -0,0 +1,31 @@
1
+ producteca/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ producteca/abstract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ producteca/abstract/abstract_dataclass.py,sha256=iAr0TlzzkJj0mVUKb2J9qZug43Le558OM6Be7e5smAI,245
4
+ producteca/client.py,sha256=EK-fl6MIHh1YgR_xHAf9xych4SXyLwIRGsz04eXLBOI,787
5
+ producteca/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ producteca/config/config.py,sha256=uTRaHLI9L7P_LPuFmuYgV50Aedz9MfDbXOZVZPFkOuI,658
7
+ producteca/payments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ producteca/payments/payments.py,sha256=ac0X-QC4CGvxs40B_qWv0IepBVpj_nULNSNp8s3v5aA,842
9
+ producteca/payments/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ producteca/payments/tests/test_payments.py,sha256=xBdZeoA5oEeQOd2rOfbrDeRFO2ALaSwRi0UTc4N8v_Y,2326
11
+ producteca/products/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ producteca/products/products.py,sha256=MZdq_ziLNMluncR40q3w7zdEf-2eaHtouuz70uUCD-Q,8664
13
+ producteca/products/search_products.py,sha256=7mWx1BapV2eGTmU1crsBLZ1i8GQCDMHa1ivuRdb8Evw,3638
14
+ producteca/products/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ producteca/products/tests/test_products.py,sha256=tUPJrRPt2biYae3I1t3vE0idHyyH6mCVMK01fhR7oSw,3169
16
+ producteca/products/tests/test_search_products.py,sha256=O926cckbstTEI4UWBOJj9k4_FThEtIM-ptp1WtliqSQ,4953
17
+ producteca/sales_orders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ producteca/sales_orders/sales_orders.py,sha256=uC02YsHQDjrAm2vTMw4K-X90Aox1KauGpGL1U7w7hWo,12169
19
+ producteca/sales_orders/search_sale_orders.py,sha256=3EQUjZSZzNjjZmY0oRa_O2zMd76EttGc5J2sgye5hpU,4420
20
+ producteca/sales_orders/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ producteca/sales_orders/tests/search.json,sha256=v6LkTvQyeBdqxRc3iWwXH5Ugs7EYJAEOiuPuuj_rHYk,3508
22
+ producteca/sales_orders/tests/test_sales_orders.py,sha256=41ud3MMS514jNpoZwQy6rYRynrt930wtg_IMRds0CnQ,3050
23
+ producteca/sales_orders/tests/test_search_so.py,sha256=Ft1c38K_Si9UUZHgbfntz8Le9Ge7Kxz9JoMTScVjp1w,1710
24
+ producteca/shipments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ producteca/shipments/shipment.py,sha256=YokJRBX1ZbUtZVc4AOAB188rbHFO0_MH5hFv5U9Qpjw,844
26
+ producteca/shipments/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ producteca/shipments/tests/test_shipment.py,sha256=0FoNApUAfIhPETuLcYWZ41Q8v_nPUg1A_-U0ebDAGmM,2073
28
+ producteca-2.0.14.dist-info/METADATA,sha256=R4puafAb4YZUIeU3GPhd-YAHh4t3c8OUKyK2MOdaJoY,3811
29
+ producteca-2.0.14.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
30
+ producteca-2.0.14.dist-info/entry_points.txt,sha256=BFSDFLbB70p8YVZPiU7HDJdj2VyyLdMVa4ekLQAUAVc,125
31
+ producteca-2.0.14.dist-info/RECORD,,
File without changes
@@ -1,170 +0,0 @@
1
- from typing import List, Optional
2
- from pydantic import BaseModel, Field
3
- import requests
4
- from producteca.config.config import ConfigProducteca
5
- import logging
6
-
7
- _logger = logging.getLogger(__name__)
8
-
9
-
10
- class SalesOrderProduct(BaseModel):
11
- id: int
12
- name: str
13
- code: str
14
- brand: str
15
-
16
-
17
- class SalesOrderVariationAttribute(BaseModel):
18
- key: str
19
- value: str
20
-
21
-
22
- class SalesOrderVariation(BaseModel):
23
- id: int
24
- attributes: List[SalesOrderVariationAttribute]
25
- sku: str
26
- thumbnail: str
27
-
28
-
29
- class SalesOrderLine(BaseModel):
30
- product: SalesOrderProduct
31
- variation: SalesOrderVariation
32
- quantity: int
33
- price: float
34
-
35
-
36
- class SalesOrderCard(BaseModel):
37
- payment_network: str
38
- first_six_digits: int
39
- last_four_digits: int
40
- cardholder_identification_number: str
41
- cardholder_identification_type: str
42
- cardholder_name: str
43
-
44
-
45
- class SalesOrderPaymentIntegration(BaseModel):
46
- integration_id: str
47
- app: int
48
-
49
-
50
- class SalesOrderPayment(BaseModel):
51
- date: str
52
- amount: float
53
- coupon_amount: float
54
- status: str
55
- method: str
56
- integration: SalesOrderPaymentIntegration
57
- transaction_fee: float
58
- installments: int
59
- card: SalesOrderCard
60
- notes: str
61
- has_cancelable_status: bool
62
- id: int
63
-
64
-
65
- class SalesOrderIntegration(BaseModel):
66
- alternate_id: str
67
- integration_id: int
68
- app: int
69
-
70
-
71
- class SalesOrderShipmentProduct(BaseModel):
72
- product: int
73
- variation: int
74
- quantity: int
75
-
76
-
77
- class SalesOrderShipmentMethod(BaseModel):
78
- tracking_number: str
79
- tracking_url: str
80
- courier: str
81
- mode: str
82
- cost: float
83
- type: str
84
- eta: str
85
- status: str
86
-
87
-
88
- class SalesOrderShipmentIntegration(BaseModel):
89
- id: int
90
- integration_id: str
91
- app: int
92
- status: str
93
-
94
-
95
- class SalesOrderShipment(BaseModel):
96
- date: str
97
- products: List[SalesOrderShipmentProduct]
98
- method: SalesOrderShipmentMethod
99
- integration: SalesOrderShipmentIntegration
100
-
101
-
102
- class SalesOrderResultItem(BaseModel):
103
- codes: List[str]
104
- contact_id: int
105
- currency: str
106
- date: str
107
- delivery_method: str
108
- delivery_status: str
109
- id: str
110
- integration_ids: List[str]
111
- integrations: List[SalesOrderIntegration]
112
- invoice_integration_app: int
113
- invoice_integration_id: str
114
- lines: List[SalesOrderLine]
115
- payments: List[SalesOrderPayment]
116
- payment_status: str
117
- payment_term: str
118
- product_names: List[str]
119
- reserving_product_ids: str
120
- sales_channel: int
121
- shipments: List[SalesOrderShipment]
122
- tracking_number: str
123
- skus: List[str]
124
- status: str
125
- tags: List[str]
126
- warehouse: str
127
- company_id: int
128
- shipping_cost: float
129
- contact_phone: str
130
- brands: List[str]
131
- courier: str
132
- order_id: int
133
- updated_at: str
134
- invoice_integration_created_at: str
135
- invoice_integration_document_url: str
136
- has_document_url: bool
137
- integration_alternate_ids: str
138
- cart_id: str
139
- amount: float
140
- has_any_shipments: bool
141
-
142
-
143
- class SearchSalesOrderResponse(BaseModel):
144
- count: int
145
- results: List[SalesOrderResultItem]
146
-
147
-
148
- class SearchSalesOrderParams(BaseModel):
149
- top: Optional[int]
150
- skip: Optional[int]
151
- filter: Optional[str] = Field(default=None, alias="$filter")
152
- class Config:
153
- validate_by_name = True
154
-
155
- class SearchSalesOrder:
156
- endpoint: str = "search/salesorders"
157
-
158
-
159
- @classmethod
160
- def search_saleorder(cls, config: ConfigProducteca, params: SearchSalesOrderParams):
161
- headers = config.headers
162
- url = config.get_endpoint(cls.endpoint)
163
- new_url = f"{url}?$filter={params.filter}&top={params.top}&skip={params.skip}"
164
- response = requests.get(
165
- new_url,
166
- headers=headers,
167
- )
168
- return response.json(), response.status_code
169
-
170
-
File without changes
@@ -1,31 +0,0 @@
1
- producteca/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- producteca/abstract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- producteca/abstract/abstract_dataclass.py,sha256=Q9BaVb34_2MBLWaOOeGuzd1diOePHxxFWBEr8Jw8t40,617
4
- producteca/client.py,sha256=oeNeLAlhjXC82oTLRN6HmWilmZqrqrs_Bwr3-u17EuI,1260
5
- producteca/config/__init__.py,sha256=ZELnRKNOj0erqtCLRtZURqUhttVbFfqUrHrgg6M5p-M,36
6
- producteca/config/config.py,sha256=uTRaHLI9L7P_LPuFmuYgV50Aedz9MfDbXOZVZPFkOuI,658
7
- producteca/payments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- producteca/payments/payments.py,sha256=bc9556954GRsvm4XAsTbbdP4-rS275MWGfhuF4MYVsY,1594
9
- producteca/payments/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- producteca/payments/tests/test_payments.py,sha256=5xUQlBT_E7arJLJAHncJz3Rme2b_JEoJ5_DzsExW4SI,2343
11
- producteca/products/__init__.py,sha256=kIkYF7_BcLUzkGvuRnUBPAELyG9QxUb5D6UiRg4XQCg,22
12
- producteca/products/products.py,sha256=IagCPEvQBUuUaw9yBQwBu7eJw3HtdAjxiWHEnpLDXwM,8046
13
- producteca/products/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- producteca/products/tests/test_products.py,sha256=AN2khGNtS7meUe4EAdgKvGadwvg9GYhP7vfI9fC1PBc,3392
15
- producteca/sales_orders/__init__.py,sha256=9NbTrJhvhtGYX2DFd7CCZvYVKXx6jJCV2L70XPma5_c,26
16
- producteca/sales_orders/sales_orders.py,sha256=1VzFSou2wOEmZ-uhehVcsj9Q4Gk5Iam4YEIu-l-xEgc,8792
17
- producteca/sales_orders/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- producteca/sales_orders/tests/test_sales_orders.py,sha256=TngA2sOuFDobqe_rWPyho8WutnzZkAKn_eYsNqh3k_A,3372
19
- producteca/search/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- producteca/search/search.py,sha256=6Xmxgs0cTK81uDTQAqAeFoWGu5pRZX-lS15tEBkRKrc,4143
21
- producteca/search/search_sale_orders.py,sha256=cTLI47HyDfPalKT-ApizK6GrVcqfMJVnyLqpJcNjm1c,3733
22
- producteca/search/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- producteca/search/tests/test_search.py,sha256=rD9hNyKscIrZHVR2rvpVGeNXX89sj2IgoH2lwYIHW2U,7454
24
- producteca/shipments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- producteca/shipments/shipment.py,sha256=IELAYWVNT862CBvDHOTotqLMXrm6sFECic2E7RTSI-A,1625
26
- producteca/shipments/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- producteca/shipments/tests/test_shipment.py,sha256=D42IM5OQOXy9Af9_p8-k_mSFnfX5fUeEWQBPEispOXw,2301
28
- producteca-1.0.13.dist-info/METADATA,sha256=lbLBx5qF8ZMVOw2VqbnyD3LN9JkbIT2oK5YyUVRHKZg,2624
29
- producteca-1.0.13.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
30
- producteca-1.0.13.dist-info/entry_points.txt,sha256=BFSDFLbB70p8YVZPiU7HDJdj2VyyLdMVa4ekLQAUAVc,125
31
- producteca-1.0.13.dist-info/RECORD,,