spyreapi 0.0.1__tar.gz → 0.0.2__tar.gz

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.
Files changed (28) hide show
  1. {spyreapi-0.0.1 → spyreapi-0.0.2}/LICENSE +1 -1
  2. spyreapi-0.0.2/MANIFEST.in +3 -0
  3. {spyreapi-0.0.1/src/spyreapi.egg-info → spyreapi-0.0.2}/PKG-INFO +8 -1
  4. {spyreapi-0.0.1 → spyreapi-0.0.2}/pyproject.toml +11 -1
  5. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/Exceptions.py +22 -21
  6. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/Models/customers_models.py +1 -1
  7. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/Models/sales_models.py +2 -2
  8. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/__init__.py +10 -2
  9. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/client.py +1 -1
  10. spyreapi-0.0.2/src/spyre/config.py +8 -0
  11. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/customers.py +3 -4
  12. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/inventory.py +110 -32
  13. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/sales.py +7 -7
  14. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/spire.py +4 -4
  15. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/utils.py +1 -1
  16. {spyreapi-0.0.1 → spyreapi-0.0.2/src/spyreapi.egg-info}/PKG-INFO +8 -1
  17. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyreapi.egg-info/SOURCES.txt +1 -0
  18. spyreapi-0.0.2/src/spyreapi.egg-info/requires.txt +7 -0
  19. {spyreapi-0.0.1 → spyreapi-0.0.2}/tests/testing.py +2 -5
  20. spyreapi-0.0.1/MANIFEST.in +0 -2
  21. spyreapi-0.0.1/src/spyre/config.py +0 -6
  22. {spyreapi-0.0.1 → spyreapi-0.0.2}/README.md +0 -0
  23. {spyreapi-0.0.1 → spyreapi-0.0.2}/setup.cfg +0 -0
  24. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/Models/__init__.py +0 -0
  25. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/Models/inventory_models.py +0 -0
  26. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyre/Models/shared_models.py +0 -0
  27. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyreapi.egg-info/dependency_links.txt +0 -0
  28. {spyreapi-0.0.1 → spyreapi-0.0.2}/src/spyreapi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) [2025] [2025]
3
+ Copyright (c) [2025] [Sanjid Sharaf]
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ global-exclude *.pyc
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spyreapi
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A robust and extensible Python client for interacting with the [Spire Business Software API](https://developer.spiresystems.com/reference). This client provides an object-oriented interface to get, create, update, delete, query, filter, sort, and manage various Spire modules such as Sales Orders, Invoices, Inventory Items, and more.
5
5
  Author-email: Sanjid Sharaf <sanjidsharaf1@gmail.com>
6
6
  License-Expression: MIT
@@ -11,6 +11,13 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.9
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: requests>=2.32.0
15
+ Requires-Dist: httpx>=0.28.0
16
+ Requires-Dist: pydantic>=2.11.0
17
+ Requires-Dist: python-dotenv>=1.1.0
18
+ Requires-Dist: pandas>=2.3.0
19
+ Requires-Dist: openpyxl>=3.1.5
20
+ Requires-Dist: numpy>=2.3.0
14
21
  Dynamic: license-file
15
22
 
16
23
  # Spire API Python Client
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spyreapi"
7
- version = "0.0.1"
7
+ version = "0.0.2"
8
8
  authors = [
9
9
  { name="Sanjid Sharaf", email="sanjidsharaf1@gmail.com" },
10
10
  ]
@@ -18,6 +18,16 @@ classifiers = [
18
18
  license = "MIT"
19
19
  license-files = ["LICEN[CS]E*"]
20
20
 
21
+ dependencies = [
22
+ "requests>=2.32.0",
23
+ "httpx>=0.28.0",
24
+ "pydantic>=2.11.0",
25
+ "python-dotenv>=1.1.0",
26
+ "pandas>=2.3.0",
27
+ "openpyxl>=3.1.5",
28
+ "numpy>=2.3.0"
29
+ ]
30
+
21
31
  [project.urls]
22
32
  Homepage = "https://github.com/sanjid-sharaf/spyre/"
23
33
  Issues = "https://github.com/sanjid-sharaf/spyre/issues"
@@ -1,23 +1,3 @@
1
-
2
- """
3
- >Cannot Convert Invoice to Quote
4
- >Inactive Item
5
- >invalide Syntax for date time
6
- >A database error has occurred:\n\nA value passed to the database was too long for the column - Country Name
7
- >Order Number Exists
8
- >Invalid Customer
9
- >Missing Parent/Child errror -> id of addresses
10
-
11
- Contacts 3
12
- Request Failure
13
- Not Found
14
- invalid Field for filter
15
-
16
-
17
- {'status_code': 422, 'url': 'https://red-wave-8362.spirelan.com:10880/api/v2/companies/intertest/sales/orders/31308/invoice',
18
- 'content': {'type': 'error', 'message': 'Cannot post a deleted order', 'traceback': '', 'error_type': 'BusinessViolationError'}}
19
- """
20
-
21
1
  class CreateRequestError(Exception):
22
2
  """
23
3
  Exception raised when a create (POST) request to the API fails.
@@ -42,4 +22,25 @@ class CreateRequestError(Exception):
42
22
  "status_code": self.status_code,
43
23
  "error_message": self.error_message,
44
24
  "response_body": self.response_body,
45
- }
25
+ }
26
+
27
+
28
+ """
29
+ Other Common Exceptions that occur during us of the Spire API includes but not limited to the following
30
+ I have yet to implement these in code and catch them while makin requests. Something to do in the Future
31
+
32
+ The following are errors that occur for the sales module
33
+ >Cannot Invoice a Quote
34
+ >Inactive Item
35
+ >Invalid Syntax for date time
36
+ >A database error has occurred:\n\nA value passed to the database was too long for the column - Country Name
37
+ >Order Number Exists
38
+ >Invalid Customer
39
+ >Missing Parent/Child errror -> id of addresses
40
+
41
+ Only 3 Contact record per an address record in the api body
42
+ Request Failure
43
+ Not Found
44
+ invalid Field for filter
45
+
46
+ """
@@ -1,6 +1,6 @@
1
1
  from typing import List, Optional, Dict, Union, Any
2
2
  from pydantic import BaseModel, model_validator
3
- from Models.shared_models import *
3
+ from .shared_models import Address
4
4
 
5
5
  class Customer(BaseModel):
6
6
  id: Optional[int] = None
@@ -1,7 +1,7 @@
1
1
  from typing import List, Optional, Dict, Union
2
2
  from pydantic import BaseModel, model_validator
3
- from Models.shared_models import *
4
- from Models.customers_models import Customer
3
+ from .shared_models import Address, Currency, Contact, Tax
4
+ from .customers_models import Customer
5
5
 
6
6
  class Inventory(BaseModel):
7
7
  id: Optional[int] = None
@@ -1,11 +1,16 @@
1
1
  from .client import SpireClient
2
- from .inventory import InventoryClient
2
+ from .spire import Spire
3
+ from .inventory import InventoryClient, ItemsClient
3
4
  from .sales import OrdersClient, InvoiceClient, salesOrder, invoice
5
+ from .customers import CustomerClient, customer
4
6
  from .Models.sales_models import SalesOrder, SalesOrderItem
5
7
  from .Models.inventory_models import InventoryItem, Vendor, UnitOfMeasure, Pricing, UPC
8
+ from .Exceptions import CreateRequestError
6
9
 
7
10
  __all__ = [
8
11
  "SpireClient",
12
+ "Spire",
13
+ "ItemsClient",
9
14
  "InventoryClient",
10
15
  "SalesOrderClient",
11
16
  "SalesOrder",
@@ -18,5 +23,8 @@ __all__ = [
18
23
  "OrdersClient",
19
24
  "InvoiceClient",
20
25
  "salesOrder",
21
- "invoice"
26
+ "invoice",
27
+ "CustomerClient",
28
+ "customer",
29
+ "CreateRequestError"
22
30
  ]
@@ -1,5 +1,5 @@
1
1
  import requests
2
- from config import BASE_URL
2
+ from .config import BASE_URL
3
3
  from typing import TypeVar, Optional, Type, Generic, List, Union, Tuple, Dict, Any
4
4
  from pydantic import BaseModel
5
5
  import json
@@ -0,0 +1,8 @@
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+
6
+ BASE_URL = os.getenv("BASE_URL")
7
+ if not BASE_URL:
8
+ raise ValueError("BASE_URL environment variable is not set. Please set it before running the application.")
@@ -1,7 +1,6 @@
1
- from client import SpireClient
2
- from Models.customers_models import Customer
3
- from client import APIResource
4
- from Exceptions import *
1
+ from .client import SpireClient, APIResource
2
+ from .Models.customers_models import Customer
3
+ from .Exceptions import CreateRequestError
5
4
  from urllib.parse import urlparse
6
5
  from typing import Optional, Dict, Any, List
7
6
 
@@ -1,25 +1,10 @@
1
- from Models.inventory_models import *
2
- import utils
3
- from client import *
1
+ from .Models.inventory_models import InventoryItem, UnitOfMeasure, UPC
2
+ from .client import SpireClient, APIResource
4
3
  from urllib.parse import urlparse
5
- from Exceptions import *
6
- from typing import Any
4
+ from .Exceptions import CreateRequestError
5
+ from typing import Any, Optional, Dict, List
7
6
  from typing import TYPE_CHECKING
8
7
 
9
- """"
10
- TODO Get Upc
11
- Create UPC
12
- Query UPC
13
- Delete UPC
14
- Update UPC
15
-
16
- Sell Prices
17
-
18
- Counts
19
- Adjustements
20
-
21
- """
22
-
23
8
  class InventoryClient():
24
9
 
25
10
  def __init__(self, client : SpireClient):
@@ -163,7 +148,19 @@ class ItemsClient():
163
148
  )
164
149
 
165
150
  def get_item_uoms(self, id : int) -> List["uom"]:
166
-
151
+ """
152
+ Retrieve all Unit of Measure (UOM) entries for a specific inventory item.
153
+
154
+ This method sends a GET request to the Spire API to retrieve the unit of measure
155
+ records associated with the specified inventory item ID. Each returned record is
156
+ wrapped into a `uom` object, which includes the model and the client reference.
157
+
158
+ Args:
159
+ id (int): The unique identifier of the inventory item.
160
+
161
+ Returns:
162
+ List[uom]: A list of `uom` objects representing the UOM records for the item.
163
+ """
167
164
  uoms = []
168
165
  response = self.client._get(f"{self.endpoint}/{str(id)}/uoms")
169
166
  items = response.get('records')
@@ -173,13 +170,43 @@ class ItemsClient():
173
170
  return uoms
174
171
 
175
172
  def get_uom(self, item_id :int , uom_id : int) -> "uom":
173
+ """
174
+ Retrieve a specific Unit of Measure (UOM) for a given inventory item.
175
+
176
+ This method sends a GET request to the Spire API to fetch a single UOM record
177
+ associated with a specific inventory item and UOM ID. The response is wrapped
178
+ into a `uom` object that maintains a reference to the API client.
179
+
180
+ Args:
181
+ item_id (int): The unique ID of the inventory item.
182
+ uom_id (int): The unique ID of the UOM to retrieve.
183
+
184
+ Returns:
185
+ uom: A `uom` object representing the retrieved unit of measure.
176
186
 
187
+ """
177
188
  response = self.client._get(f"/{self.endpoint}/{str(item_id)}/uoms/{str(uom_id)}")
178
189
  return uom.from_json(response, self.client)
179
190
 
180
191
 
181
192
  def create_item_uom(self, id: int, uom : UnitOfMeasure) -> List["uom"]:
182
-
193
+ """
194
+ Create a new Unit of Measure (UOM) for a specific inventory item.
195
+
196
+ Sends a POST request to the Spire API to create a new UOM for the inventory item
197
+ with the specified ID. If successful, the method retrieves and returns the newly
198
+ created UOM object. If the creation fails, an exception is raised.
199
+
200
+ Args:
201
+ id (int): The ID of the inventory item for which to create the UOM.
202
+ uom (UnitOfMeasure): A Pydantic model instance representing the UOM to create.
203
+
204
+ Returns:
205
+ List[uom]: A list containing the created `uom` object.
206
+
207
+ Raises:
208
+ CreateRequestError: If the API response status is not 201 (Created).
209
+ """
183
210
  response = self.client._post(f"/{self.endpoint}/{str(id)}/uoms", json=uom.model_dump(exclude_unset=True, exclude_none=True))
184
211
  if response.get('status_code') == 201:
185
212
  location = response.get('headers').get('location')
@@ -192,17 +219,53 @@ class ItemsClient():
192
219
  raise CreateRequestError(self.endpoint, status_code=response.get('status_code'), error_message=error_message)
193
220
 
194
221
  def delete_uom(self, item_id : int, uom_id :int) -> bool:
222
+ """
223
+ Delete a Unit of Measure (UOM) from a specific inventory item.
224
+
225
+ Sends a DELETE request to the Spire API to remove a UOM associated with the
226
+ given item and UOM ID.
195
227
 
228
+ Args:
229
+ item_id (int): The ID of the inventory item.
230
+ uom_id (int): The ID of the Unit of Measure to delete.
231
+
232
+ Returns:
233
+ bool: True if the deletion was successful, otherwise raises an error.
234
+ """
196
235
  return self.client._delete(f"/{self.endpoint}/{str(item_id)}/uoms/{str(uom_id)}")
197
236
 
198
237
  def update_item_uom(self, item_id: int, uom_id : int, uom :'uom') -> 'uom':
238
+ """
239
+ Update an existing Unit of Measure (UOM) for a given inventory item.
240
+
241
+ Sends a PUT request to the Spire API to update the UOM record with the specified
242
+ item and UOM ID using the provided `uom` data.
199
243
 
244
+ Args:
245
+ item_id (int): The ID of the inventory item.
246
+ uom_id (int): The ID of the UOM to update.
247
+ uom (uom): A `uom` instance with updated field values.
248
+
249
+ Returns:
250
+ uom: The updated `uom` instance returned from the API.
251
+ """
200
252
  response = self.client._put(f"/{self.endpoint}/{str(item_id)}/{str(uom_id)}", json=uom.model_dump(exclude_none=True, exclude_unset=True))
201
253
  return uom.from_json(response, self.client)
202
254
 
203
255
 
204
256
  def get_item_upcs(self, id : int) -> List["upc"]:
205
-
257
+ """
258
+ Retrieve all UPC records associated with a specific inventory item.
259
+
260
+ Sends a GET request to the Spire API to fetch UPCs for the specified item ID,
261
+ then constructs and returns a list of `upc` objects.
262
+
263
+ Args:
264
+ id (int): The ID of the inventory item.
265
+
266
+ Returns:
267
+ List[upc]: A list of `upc` objects representing the item's UPC codes.
268
+ """
206
269
  upcs = []
207
270
  response = self.client._get(f"{self.endpoint}/{str(id)}/upcs")
208
271
  items = response.get('records')
@@ -212,11 +275,6 @@ class ItemsClient():
212
275
  return upcs
213
276
 
214
277
 
215
- # def get_uom(self, item_id :int , uom_id : int) -> "uom":
216
-
217
- # response = self.client._get(f"/{self.endpoint}/{str(item_id)}/uoms/{str(uom_id)}")
218
- # return uom.from_json(response, self.client)
219
-
220
278
  class item(APIResource[InventoryItem]):
221
279
 
222
280
  endpoint = 'inventory/items'
@@ -257,7 +315,15 @@ class item(APIResource[InventoryItem]):
257
315
  return item.from_json(response, self._client)
258
316
 
259
317
  def get_uoms(self) -> List["uom"]:
260
-
318
+ """
319
+ Retrieve all Unit of Measure (UOM) records for the current inventory item.
320
+
321
+ This method sends a GET request to the Spire API and returns all UOMs
322
+ associated with this item's ID.
323
+
324
+ Returns:
325
+ List[uom]: A list of `uom` instances representing the available units of measure.
326
+ """
261
327
  uoms = []
262
328
  response = self._client._get(f"{self.endpoint}/{self.id}/uoms")
263
329
  items = response.get('records')
@@ -268,7 +334,22 @@ class item(APIResource[InventoryItem]):
268
334
 
269
335
 
270
336
  def add_uom(self, uom : UnitOfMeasure) -> List["uom"]:
271
-
337
+ """
338
+ Add a new Unit of Measure (UOM) to the current inventory item.
339
+
340
+ Sends a POST request to the Spire API to create a new UOM record using the provided
341
+ `UnitOfMeasure` model. If the creation is successful (HTTP 201), it retrieves
342
+ and returns the created UOM.
343
+
344
+ Args:
345
+ uom (UnitOfMeasure): The UnitOfMeasure Pydantic model to be created.
346
+
347
+ Returns:
348
+ List[uom]: A list containing the newly created `uom` instance.
349
+
350
+ Raises:
351
+ CreateRequestError: If the API returns a non-201 status code during creation.
352
+ """
272
353
  response = self.client._post(f"/{self.endpoint}/{str(self.id)}/uoms", json=uom.model_dump(exclude_unset=True, exclude_none=True))
273
354
  if response.get('status_code') == 201:
274
355
  location = response.get('headers').get('location')
@@ -328,9 +409,6 @@ class UpcClient():
328
409
  def __init__(self, client : SpireClient):
329
410
  self.endpoint = 'inventory/upcs'
330
411
 
331
-
332
-
333
-
334
412
  class upc(APIResource[UPC]):
335
413
  _endpoint = ''
336
414
  Model = ''
@@ -1,10 +1,10 @@
1
- from client import APIResource
2
- from Models.sales_models import *
3
- import utils
4
- from client import SpireClient
1
+ from .client import APIResource
2
+ from .Models.sales_models import SalesOrder, SalesOrderItem, Invoice
3
+ from .utils import *
4
+ from .client import SpireClient
5
5
  from urllib.parse import urlparse
6
- from Exceptions import *
7
- from typing import Any
6
+ from .Exceptions import CreateRequestError
7
+ from typing import Any, Optional, List, Dict
8
8
 
9
9
  class OrdersClient():
10
10
 
@@ -292,7 +292,7 @@ class invoice(APIResource[Invoice]):
292
292
  Returns:
293
293
  salesOrder: The created sales order instance returned by the API.
294
294
  """
295
- order_converted = utils.create_sales_order_from_invoice(self._model)
295
+ order_converted = create_sales_order_from_invoice(self._model)
296
296
  return OrdersClient(self._client).create_sales_order(order_converted)
297
297
 
298
298
  def update(self , invoice_: "Invoice" = None) -> 'invoice':
@@ -1,7 +1,7 @@
1
- from client import SpireClient
2
- from sales import *
3
- from customers import *
4
- from inventory import InventoryClient
1
+ from .client import SpireClient
2
+ from .sales import OrdersClient, InvoiceClient
3
+ from .customers import CustomerClient
4
+ from .inventory import InventoryClient
5
5
 
6
6
  class Spire:
7
7
  def __init__(self, company : str, username : str, password : str):
@@ -1,4 +1,4 @@
1
- from Models.sales_models import *
1
+ from .Models.sales_models import SalesOrder, Invoice
2
2
  from typing import TypeVar, Optional, Type, List, Set
3
3
  from copy import deepcopy
4
4
  from pydantic import BaseModel
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spyreapi
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A robust and extensible Python client for interacting with the [Spire Business Software API](https://developer.spiresystems.com/reference). This client provides an object-oriented interface to get, create, update, delete, query, filter, sort, and manage various Spire modules such as Sales Orders, Invoices, Inventory Items, and more.
5
5
  Author-email: Sanjid Sharaf <sanjidsharaf1@gmail.com>
6
6
  License-Expression: MIT
@@ -11,6 +11,13 @@ Classifier: Operating System :: OS Independent
11
11
  Requires-Python: >=3.9
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
+ Requires-Dist: requests>=2.32.0
15
+ Requires-Dist: httpx>=0.28.0
16
+ Requires-Dist: pydantic>=2.11.0
17
+ Requires-Dist: python-dotenv>=1.1.0
18
+ Requires-Dist: pandas>=2.3.0
19
+ Requires-Dist: openpyxl>=3.1.5
20
+ Requires-Dist: numpy>=2.3.0
14
21
  Dynamic: license-file
15
22
 
16
23
  # Spire API Python Client
@@ -19,5 +19,6 @@ src/spyre/Models/shared_models.py
19
19
  src/spyreapi.egg-info/PKG-INFO
20
20
  src/spyreapi.egg-info/SOURCES.txt
21
21
  src/spyreapi.egg-info/dependency_links.txt
22
+ src/spyreapi.egg-info/requires.txt
22
23
  src/spyreapi.egg-info/top_level.txt
23
24
  tests/testing.py
@@ -0,0 +1,7 @@
1
+ requests>=2.32.0
2
+ httpx>=0.28.0
3
+ pydantic>=2.11.0
4
+ python-dotenv>=1.1.0
5
+ pandas>=2.3.0
6
+ openpyxl>=3.1.5
7
+ numpy>=2.3.0
@@ -1,8 +1,5 @@
1
- from spyre.spire import Spire
2
- from spyre.Models.sales_models import *
3
- import json
4
- from spyre.utils import *
5
- from spyre.customers import customer
1
+ import spyre
2
+ import spyre.spire
6
3
 
7
4
  """
8
5
  Tested
@@ -1,2 +0,0 @@
1
- include README.md
2
- include LICENSE
@@ -1,6 +0,0 @@
1
- import os
2
- from dotenv import load_dotenv
3
-
4
- load_dotenv()
5
-
6
- BASE_URL = os.getenv("BASE_URL")
File without changes
File without changes