spyreapi 0.0.1__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.
- spyre/Exceptions.py +45 -0
- spyre/Models/__init__.py +0 -0
- spyre/Models/customers_models.py +40 -0
- spyre/Models/inventory_models.py +160 -0
- spyre/Models/sales_models.py +170 -0
- spyre/Models/shared_models.py +96 -0
- spyre/__init__.py +22 -0
- spyre/client.py +208 -0
- spyre/config.py +6 -0
- spyre/customers.py +157 -0
- spyre/inventory.py +344 -0
- spyre/sales.py +313 -0
- spyre/spire.py +14 -0
- spyre/utils.py +130 -0
- spyreapi-0.0.1.dist-info/METADATA +37 -0
- spyreapi-0.0.1.dist-info/RECORD +19 -0
- spyreapi-0.0.1.dist-info/WHEEL +5 -0
- spyreapi-0.0.1.dist-info/licenses/LICENSE +21 -0
- spyreapi-0.0.1.dist-info/top_level.txt +1 -0
spyre/Exceptions.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
class CreateRequestError(Exception):
|
|
22
|
+
"""
|
|
23
|
+
Exception raised when a create (POST) request to the API fails.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, endpoint: str, status_code: int, error_message: str = "", response_body: dict = None):
|
|
27
|
+
self.endpoint = endpoint
|
|
28
|
+
self.status_code = status_code
|
|
29
|
+
self.error_message = error_message or "Unknown error occurred"
|
|
30
|
+
self.response_body = response_body or {}
|
|
31
|
+
|
|
32
|
+
super().__init__(self.__str__())
|
|
33
|
+
|
|
34
|
+
def __str__(self):
|
|
35
|
+
return (
|
|
36
|
+
f"Create request to '{self.endpoint}' failed with status {self.status_code}: {self.error_message}"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def to_dict(self):
|
|
40
|
+
return {
|
|
41
|
+
"endpoint": self.endpoint,
|
|
42
|
+
"status_code": self.status_code,
|
|
43
|
+
"error_message": self.error_message,
|
|
44
|
+
"response_body": self.response_body,
|
|
45
|
+
}
|
spyre/Models/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import List, Optional, Dict, Union, Any
|
|
2
|
+
from pydantic import BaseModel, model_validator
|
|
3
|
+
from Models.shared_models import *
|
|
4
|
+
|
|
5
|
+
class Customer(BaseModel):
|
|
6
|
+
id: Optional[int] = None
|
|
7
|
+
code: Optional[str] = None
|
|
8
|
+
customerNo: Optional[str] = None
|
|
9
|
+
name: Optional[str] = None
|
|
10
|
+
foregroundColor: Optional[int] = None
|
|
11
|
+
backgroundColor: Optional[int] = None
|
|
12
|
+
hold: Optional[bool] = None
|
|
13
|
+
status: Optional[str] = None
|
|
14
|
+
reference: Optional[str] = None
|
|
15
|
+
address: Optional[Address] = None
|
|
16
|
+
shippingAddresses: Optional[List[Address]] = None
|
|
17
|
+
paymentTerms: Optional[Dict[str, Any]] = None
|
|
18
|
+
applyFinanceCharges: Optional[bool] = None
|
|
19
|
+
statementType: Optional[str] = None
|
|
20
|
+
creditType: Optional[int] = None
|
|
21
|
+
creditLimit: Optional[str] = None
|
|
22
|
+
creditBalance: Optional[str] = None
|
|
23
|
+
creditApprovedBy: Optional[str] = None
|
|
24
|
+
creditApprovedDate: Optional[str] = None
|
|
25
|
+
currency: Optional[str] = None
|
|
26
|
+
userDef1: Optional[str] = None
|
|
27
|
+
userDef2: Optional[str] = None
|
|
28
|
+
discount: Optional[str] = None
|
|
29
|
+
receivableAccount: Optional[str] = None
|
|
30
|
+
defaultShipTo: Optional[str] = None
|
|
31
|
+
specialCode: Optional[str] = None
|
|
32
|
+
upload: Optional[bool] = None
|
|
33
|
+
lastModified: Optional[str] = None
|
|
34
|
+
paymentProviderId: Optional[int] = None
|
|
35
|
+
udf: Optional[Dict[str, Any]] = None
|
|
36
|
+
createdBy: Optional[str] = None
|
|
37
|
+
modifiedBy: Optional[str] = None
|
|
38
|
+
created: Optional[str] = None
|
|
39
|
+
modified: Optional[str] = None
|
|
40
|
+
links: Optional[Dict[str, str]] = None
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from typing import List, Dict, Optional, Union
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UPC(BaseModel):
|
|
7
|
+
id: Optional[int] = None
|
|
8
|
+
whse: Optional[str] = None
|
|
9
|
+
partNo: Optional[str] = None
|
|
10
|
+
inventory: Optional[Dict] = None
|
|
11
|
+
uomCode: Optional[str] = None
|
|
12
|
+
upc: Optional[str] = None
|
|
13
|
+
created: Optional[str] = None
|
|
14
|
+
createdBy: Optional[str] = None
|
|
15
|
+
modified: Optional[str] = None
|
|
16
|
+
modifiedBy: Optional[str] = None
|
|
17
|
+
links: Optional[Dict[str, str]] = None
|
|
18
|
+
|
|
19
|
+
class Vendor(BaseModel):
|
|
20
|
+
id: Optional[int] = None
|
|
21
|
+
vendorNo: Optional[str] = None
|
|
22
|
+
name: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class UnitOfMeasure(BaseModel):
|
|
26
|
+
id: Optional[int] = None
|
|
27
|
+
code: Optional[str] = None
|
|
28
|
+
description: Optional[str] = None
|
|
29
|
+
location: Optional[str] = None
|
|
30
|
+
weight: Optional[str] = None
|
|
31
|
+
buyUOM: Optional[bool] = None
|
|
32
|
+
sellUOM: Optional[bool] = None
|
|
33
|
+
allowFractionalQty: Optional[bool] = None
|
|
34
|
+
quantityFactor: Optional[str] = None
|
|
35
|
+
directFactor: Optional[bool] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Pricing(BaseModel):
|
|
39
|
+
id: Optional[int] = None
|
|
40
|
+
sellPrices: Optional[List[str]] = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ItemUDF(BaseModel):
|
|
44
|
+
desc: Optional[str] = None
|
|
45
|
+
brand: Optional[str] = None
|
|
46
|
+
model: Optional[str] = None
|
|
47
|
+
title: Optional[str] = None
|
|
48
|
+
online: Optional[bool] = None
|
|
49
|
+
pk_qty: Optional[str] = None
|
|
50
|
+
volume: Optional[str] = None
|
|
51
|
+
presale: Optional[bool] = None
|
|
52
|
+
sds_url: Optional[str] = None
|
|
53
|
+
tds_url: Optional[str] = None
|
|
54
|
+
features: Optional[str] = None
|
|
55
|
+
includes: Optional[str] = None
|
|
56
|
+
keywords: Optional[str] = None
|
|
57
|
+
op1_name: Optional[str] = None
|
|
58
|
+
op2_name: Optional[str] = None
|
|
59
|
+
op3_name: Optional[str] = None
|
|
60
|
+
op4_name: Optional[str] = None
|
|
61
|
+
asm_depth: Optional[str] = None
|
|
62
|
+
asm_width: Optional[str] = None
|
|
63
|
+
category1: Optional[str] = None
|
|
64
|
+
category2: Optional[str] = None
|
|
65
|
+
category3: Optional[str] = None
|
|
66
|
+
category4: Optional[str] = None
|
|
67
|
+
op1_value: Optional[str] = None
|
|
68
|
+
op2_value: Optional[str] = None
|
|
69
|
+
op3_value: Optional[str] = None
|
|
70
|
+
op4_value: Optional[str] = None
|
|
71
|
+
pkg_depth: Optional[str] = None
|
|
72
|
+
pkg_width: Optional[str] = None
|
|
73
|
+
asm_height: Optional[str] = None
|
|
74
|
+
asm_length: Optional[str] = None
|
|
75
|
+
asm_weight: Optional[str] = None
|
|
76
|
+
html_specs: Optional[str] = None
|
|
77
|
+
image_urls: Optional[str] = None
|
|
78
|
+
is_variant: Optional[bool] = None
|
|
79
|
+
pkg_height: Optional[str] = None
|
|
80
|
+
pkg_length: Optional[str] = None
|
|
81
|
+
pkg_weight: Optional[str] = None
|
|
82
|
+
put_online: Optional[bool] = None
|
|
83
|
+
upc_needed: Optional[bool] = None
|
|
84
|
+
video_urls: Optional[str] = None
|
|
85
|
+
hdr_part_no: Optional[str] = None
|
|
86
|
+
applications: Optional[str] = None
|
|
87
|
+
sell_through: Optional[bool] = None
|
|
88
|
+
unique_title: Optional[str] = None
|
|
89
|
+
alternate_res: Optional[str] = None
|
|
90
|
+
asm_weight_op: Optional[str] = None
|
|
91
|
+
html_includes: Optional[str] = None
|
|
92
|
+
more_info_url: Optional[str] = None
|
|
93
|
+
specsheet_url: Optional[str] = None
|
|
94
|
+
html_desc_feat: Optional[str] = None
|
|
95
|
+
specifications: Optional[str] = None
|
|
96
|
+
html_dimensions: Optional[str] = None
|
|
97
|
+
special_delivery: Optional[bool] = None
|
|
98
|
+
alternate_res_src: Optional[str] = None
|
|
99
|
+
html_applications: Optional[str] = None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class InventoryItem(BaseModel):
|
|
103
|
+
id: Optional[int] = None
|
|
104
|
+
whse: Optional[str] = None
|
|
105
|
+
partNo: Optional[str] = None
|
|
106
|
+
description: Optional[str] = None
|
|
107
|
+
type: Optional[str] = None
|
|
108
|
+
status: Optional[int] = None
|
|
109
|
+
lotNumbered: Optional[bool] = None
|
|
110
|
+
serialized: Optional[bool] = None
|
|
111
|
+
availableQty: Optional[str] = None
|
|
112
|
+
onHandQty: Optional[str] = None
|
|
113
|
+
committedQty: Optional[str] = None
|
|
114
|
+
backorderQty: Optional[str] = None
|
|
115
|
+
onPurchaseQty: Optional[str] = None
|
|
116
|
+
foregroundColor: Optional[int] = None
|
|
117
|
+
backgroundColor: Optional[int] = None
|
|
118
|
+
primaryVendor: Optional[Vendor] = None
|
|
119
|
+
currentPONo: Optional[str] = None
|
|
120
|
+
poDueDate: Optional[str] = None
|
|
121
|
+
reorderPoint: Optional[str] = None
|
|
122
|
+
minimumBuyQty: Optional[str] = None
|
|
123
|
+
currentCost: Optional[str] = None
|
|
124
|
+
averageCost: Optional[str] = None
|
|
125
|
+
standardCost: Optional[str] = None
|
|
126
|
+
unitOfMeasures: Optional[Dict[str, UnitOfMeasure]] = None
|
|
127
|
+
buyMeasureCode: Optional[str] = None
|
|
128
|
+
stockMeasureCode: Optional[str] = None
|
|
129
|
+
sellMeasureCode: Optional[str] = None
|
|
130
|
+
alternatePartNo: Optional[str] = None
|
|
131
|
+
productCode: Optional[str] = None
|
|
132
|
+
groupNo: Optional[str] = None
|
|
133
|
+
salesDept: Optional[str] = None
|
|
134
|
+
userDef1: Optional[str] = None
|
|
135
|
+
userDef2: Optional[str] = None
|
|
136
|
+
discountable: Optional[bool] = None
|
|
137
|
+
weight: Optional[str] = None
|
|
138
|
+
packSize: Optional[str] = None
|
|
139
|
+
allowBackorders: Optional[bool] = None
|
|
140
|
+
allowReturns: Optional[bool] = None
|
|
141
|
+
dutyPct: Optional[str] = None
|
|
142
|
+
freightPct: Optional[str] = None
|
|
143
|
+
manufactureCountry: Optional[str] = None
|
|
144
|
+
harmonizedCode: Optional[str] = None
|
|
145
|
+
extendedDescription: Optional[str] = None
|
|
146
|
+
pricing: Optional[Dict[str, Pricing]] = None
|
|
147
|
+
salesTaxFlags: Optional[Dict[str, Union[str, int, float, bool]]] = None
|
|
148
|
+
images: Optional[List[str]] = None
|
|
149
|
+
defaultExpiryDate: Optional[str] = None
|
|
150
|
+
lotConsumeType: Optional[str] = None
|
|
151
|
+
upload: Optional[bool] = None
|
|
152
|
+
showOptions: Optional[bool] = None
|
|
153
|
+
lastModified: Optional[str] = None
|
|
154
|
+
levy: Optional[str] = None
|
|
155
|
+
udf: Optional[ItemUDF] = None
|
|
156
|
+
createdBy: Optional[str] = None
|
|
157
|
+
modifiedBy: Optional[str] = None
|
|
158
|
+
created: Optional[str] = None
|
|
159
|
+
modified: Optional[str] = None
|
|
160
|
+
links: Optional[Dict[str, Union[str, int, float, bool]]] = None
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from typing import List, Optional, Dict, Union
|
|
2
|
+
from pydantic import BaseModel, model_validator
|
|
3
|
+
from Models.shared_models import *
|
|
4
|
+
from Models.customers_models import Customer
|
|
5
|
+
|
|
6
|
+
class Inventory(BaseModel):
|
|
7
|
+
id: Optional[int] = None
|
|
8
|
+
whse: Optional[str] = None
|
|
9
|
+
partNo: Optional[str] = None
|
|
10
|
+
description: Optional[str] = None
|
|
11
|
+
|
|
12
|
+
class SalesOrderItem(BaseModel):
|
|
13
|
+
id: Optional[int] = None
|
|
14
|
+
orderNo: Optional[str] = None
|
|
15
|
+
sequence: Optional[int] = None
|
|
16
|
+
parentSequence: Optional[int] = None
|
|
17
|
+
inventory: Optional[Inventory] = None
|
|
18
|
+
serials: Optional[str] = None
|
|
19
|
+
whse: Optional[str] = None
|
|
20
|
+
partNo: Optional[str] = None
|
|
21
|
+
description: Optional[str] = None
|
|
22
|
+
comment: Optional[str] = None
|
|
23
|
+
orderQty: Optional[str] = None
|
|
24
|
+
committedQty: Optional[str] = None
|
|
25
|
+
backorderQty: Optional[str] = None
|
|
26
|
+
sellMeasure: Optional[str] = None
|
|
27
|
+
retailPrice: Optional[str] = None
|
|
28
|
+
unitPrice: Optional[str] = None
|
|
29
|
+
userPrice: Optional[bool] = None
|
|
30
|
+
discountable: Optional[bool] = None
|
|
31
|
+
discountPct: Optional[str] = None
|
|
32
|
+
discountAmt: Optional[str] = None
|
|
33
|
+
currentCost: Optional[str] = None
|
|
34
|
+
averageCost: Optional[str] = None
|
|
35
|
+
standardCost: Optional[str] = None
|
|
36
|
+
taxFlags: Optional[List[bool]] = None
|
|
37
|
+
vendor: Optional[str] = None
|
|
38
|
+
inventoryAccountNo: Optional[str] = None
|
|
39
|
+
revenueAccountNo: Optional[str] = None
|
|
40
|
+
costOfGoodsAccountNo: Optional[str] = None
|
|
41
|
+
levyCode: Optional[str] = None
|
|
42
|
+
referenceNo: Optional[str] = None
|
|
43
|
+
requiredDate: Optional[str] = None
|
|
44
|
+
extendedPriceOrdered: Optional[str] = None
|
|
45
|
+
extendedPriceCommitted: Optional[str] = None
|
|
46
|
+
kit: Optional[bool] = None
|
|
47
|
+
suppress: Optional[bool] = None
|
|
48
|
+
udf: Optional[dict] = None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SalesOrder(BaseModel):
|
|
52
|
+
id: Optional[int] = None
|
|
53
|
+
orderNo: Optional[str] = None
|
|
54
|
+
division: Optional[str] = None
|
|
55
|
+
location: Optional[str] = None
|
|
56
|
+
profitCenter: Optional[str] = None
|
|
57
|
+
invoiceNo: Optional[str] = None
|
|
58
|
+
customer: Optional[Customer] = None
|
|
59
|
+
creditApprovedAmount: Optional[str] = None
|
|
60
|
+
creditApprovedDate: Optional[str] = None
|
|
61
|
+
creditApprovedUser: Optional[str] = None
|
|
62
|
+
currency: Optional[Currency] = None
|
|
63
|
+
status: Optional[str] = None
|
|
64
|
+
type: Optional[str] = None
|
|
65
|
+
hold: Optional[bool] = None
|
|
66
|
+
orderDate: Optional[str] = None
|
|
67
|
+
invoiceDate: Optional[str] = None
|
|
68
|
+
requiredDate: Optional[str] = None
|
|
69
|
+
quoteExpires: Optional[str] = None
|
|
70
|
+
recurrenceRule: Optional[str] = None
|
|
71
|
+
address: Optional[Address] = None
|
|
72
|
+
shippingAddress: Optional[Address] = None
|
|
73
|
+
contact: Optional[Contact] = None
|
|
74
|
+
customerPO: Optional[str] = None
|
|
75
|
+
batchNo: Optional[str] = None
|
|
76
|
+
fob: Optional[str] = None
|
|
77
|
+
incoterms: Optional[str] = None
|
|
78
|
+
incotermsPlace: Optional[str] = None
|
|
79
|
+
referenceNo: Optional[str] = None
|
|
80
|
+
shippingCarrier: Optional[str] = None
|
|
81
|
+
shipDate: Optional[str] = None
|
|
82
|
+
trackingNo: Optional[str] = None
|
|
83
|
+
termsCode: Optional[str] = None
|
|
84
|
+
termsText: Optional[str] = None
|
|
85
|
+
freight: Optional[str] = None
|
|
86
|
+
taxes: Optional[List[Tax]] = None
|
|
87
|
+
subtotal: Optional[str] = None
|
|
88
|
+
subtotalOrdered: Optional[str] = None
|
|
89
|
+
discount: Optional[str] = None
|
|
90
|
+
totalDiscount: Optional[str] = None
|
|
91
|
+
total: Optional[str] = None
|
|
92
|
+
totalOrdered: Optional[str] = None
|
|
93
|
+
totalCostCurrent: Optional[str] = None
|
|
94
|
+
totalCostAverage: Optional[str] = None
|
|
95
|
+
grossProfit: Optional[str] = None
|
|
96
|
+
items: Optional[List[SalesOrderItem]] = None
|
|
97
|
+
payments: Optional[List[dict]] = None
|
|
98
|
+
udf: Optional[dict] = None
|
|
99
|
+
createdBy: Optional[str] = None
|
|
100
|
+
modifiedBy: Optional[str] = None
|
|
101
|
+
created: Optional[str] = None
|
|
102
|
+
modified: Optional[str] = None
|
|
103
|
+
deletedBy: Optional[str] = None
|
|
104
|
+
deleted: Optional[str] = None
|
|
105
|
+
links: Optional[Dict[str, str]] = None
|
|
106
|
+
|
|
107
|
+
@model_validator(mode="before")
|
|
108
|
+
@classmethod
|
|
109
|
+
def clean_problematic_fields(cls, data: dict) -> dict:
|
|
110
|
+
if data.get("currency") == "":
|
|
111
|
+
data["currency"] = None
|
|
112
|
+
|
|
113
|
+
contact = data.get("contact")
|
|
114
|
+
if contact:
|
|
115
|
+
for field in ("phone", "fax"):
|
|
116
|
+
phone_data = contact.get(field)
|
|
117
|
+
if isinstance(phone_data, dict) and phone_data.get("number") is None:
|
|
118
|
+
phone_data["number"] = ""
|
|
119
|
+
return data
|
|
120
|
+
|
|
121
|
+
class Invoice(BaseModel):
|
|
122
|
+
id: Optional[int] = None
|
|
123
|
+
invoiceNo: Optional[str] = None
|
|
124
|
+
orderNo: Optional[str] = None
|
|
125
|
+
division: Optional[str] = None
|
|
126
|
+
location: Optional[str] = None
|
|
127
|
+
profitCenter: Optional[str] = None
|
|
128
|
+
customer: Optional['Customer'] = None
|
|
129
|
+
currency: Optional['Currency'] = None
|
|
130
|
+
orderDate: Optional[str] = None
|
|
131
|
+
invoiceDate: Optional[str] = None
|
|
132
|
+
requiredDate: Optional[str] = None
|
|
133
|
+
address: Optional['Address'] = None
|
|
134
|
+
shippingAddress: Optional['Address'] = None
|
|
135
|
+
customerPO: Optional[str] = None
|
|
136
|
+
fob: Optional[str] = None
|
|
137
|
+
incoterms: Optional[str] = None
|
|
138
|
+
incotermsPlace: Optional[str] = None
|
|
139
|
+
referenceNo: Optional[str] = None
|
|
140
|
+
shippingCarrier: Optional[str] = None
|
|
141
|
+
shipDate: Optional[str] = None
|
|
142
|
+
trackingNo: Optional[str] = None
|
|
143
|
+
termsCode: Optional[str] = None
|
|
144
|
+
termsText: Optional[str] = None
|
|
145
|
+
freight: Optional[str] = None
|
|
146
|
+
taxes: Optional[List['Tax']] = None
|
|
147
|
+
subtotal: Optional[str] = None
|
|
148
|
+
total: Optional[str] = None
|
|
149
|
+
items: Optional[List['SalesOrderItem']] = None
|
|
150
|
+
payments: Optional[List[dict]] = None
|
|
151
|
+
udf: Optional[dict] = None
|
|
152
|
+
createdBy: Optional[str] = None
|
|
153
|
+
modifiedBy: Optional[str] = None
|
|
154
|
+
created: Optional[str] = None
|
|
155
|
+
modified: Optional[str] = None
|
|
156
|
+
links: Optional[Dict[str, str]] = None
|
|
157
|
+
|
|
158
|
+
@model_validator(mode="before")
|
|
159
|
+
@classmethod
|
|
160
|
+
def clean_problematic_fields(cls, data: dict) -> dict:
|
|
161
|
+
if data.get("currency") == "":
|
|
162
|
+
data["currency"] = None
|
|
163
|
+
|
|
164
|
+
contact = data.get("contact")
|
|
165
|
+
if contact:
|
|
166
|
+
for field in ("phone", "fax"):
|
|
167
|
+
phone_data = contact.get(field)
|
|
168
|
+
if isinstance(phone_data, dict) and phone_data.get("number") is None:
|
|
169
|
+
phone_data["number"] = ""
|
|
170
|
+
return data
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import List, Optional, Dict, Union
|
|
2
|
+
from pydantic import BaseModel, model_validator
|
|
3
|
+
|
|
4
|
+
class PhoneFax(BaseModel):
|
|
5
|
+
number: str
|
|
6
|
+
format: Optional[int] = 1
|
|
7
|
+
|
|
8
|
+
class Contact(BaseModel):
|
|
9
|
+
id: Optional[int] = None
|
|
10
|
+
contact_type: Optional[dict] = None
|
|
11
|
+
name: Optional[str] = None
|
|
12
|
+
email: Optional[str] = None
|
|
13
|
+
phone: Optional[PhoneFax] = None
|
|
14
|
+
fax: Optional[PhoneFax] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Salesperson(BaseModel):
|
|
18
|
+
code: Optional[str] = None
|
|
19
|
+
name: Optional[str] = None
|
|
20
|
+
|
|
21
|
+
class Territory(BaseModel):
|
|
22
|
+
code: Optional[str] = None
|
|
23
|
+
description: Optional[str] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Currency(BaseModel):
|
|
27
|
+
id: Optional[int] = None
|
|
28
|
+
code: Optional[str] = None
|
|
29
|
+
description: Optional[str] = None
|
|
30
|
+
country: Optional[str] = None
|
|
31
|
+
units: Optional[str] = None
|
|
32
|
+
fraction: Optional[str] = None
|
|
33
|
+
symbol: Optional[str] = None
|
|
34
|
+
decimalPlaces: Optional[int] = None
|
|
35
|
+
symbolPosition: Optional[str] = None
|
|
36
|
+
rate: Optional[str] = None
|
|
37
|
+
rateMethod: Optional[str] = None
|
|
38
|
+
glAccountNo: Optional[str] = None
|
|
39
|
+
thousandsSeparator: Optional[str] = None
|
|
40
|
+
lastYearRate: Optional[List[str]] = None
|
|
41
|
+
thisYearRate: Optional[List[str]] = None
|
|
42
|
+
nextYearRate: Optional[List[str]] = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Tax(BaseModel):
|
|
46
|
+
code: Optional[int] = None
|
|
47
|
+
name: Optional[str] = None
|
|
48
|
+
shortName: Optional[str] = None
|
|
49
|
+
rate: Optional[Union[str, int]] = None
|
|
50
|
+
exemptNo: Optional[str] = None
|
|
51
|
+
total: Optional[Union[str, int]] = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Address(BaseModel):
|
|
55
|
+
id: Optional[int] = None
|
|
56
|
+
type: Optional[str] = None
|
|
57
|
+
linkTable: Optional[str] = None
|
|
58
|
+
linkType: Optional[str] = None
|
|
59
|
+
linkNo: Optional[str] = None
|
|
60
|
+
shipId: Optional[str] = None
|
|
61
|
+
name: Optional[str] = None
|
|
62
|
+
line1: Optional[str] = None
|
|
63
|
+
line2: Optional[str] = None
|
|
64
|
+
line3: Optional[str] = None
|
|
65
|
+
line4: Optional[str] = None
|
|
66
|
+
city: Optional[str] = None
|
|
67
|
+
postalCode: Optional[str] = None
|
|
68
|
+
provState: Optional[str] = None
|
|
69
|
+
country: Optional[str] = None
|
|
70
|
+
phone: Optional[PhoneFax] = None
|
|
71
|
+
fax: Optional[PhoneFax] = None
|
|
72
|
+
email: Optional[str] = None
|
|
73
|
+
website: Optional[str] = None
|
|
74
|
+
shipCode: Optional[str] = None
|
|
75
|
+
shipDescription: Optional[str] = None
|
|
76
|
+
salesperson: Optional[Salesperson] = None
|
|
77
|
+
territory: Optional[Territory] = None
|
|
78
|
+
sellLevel: Optional[int] = None
|
|
79
|
+
glAccount: Optional[str] = None
|
|
80
|
+
defaultWarehouse: Optional[str] = None
|
|
81
|
+
udf: Optional[dict] = None
|
|
82
|
+
created: Optional[str] = None
|
|
83
|
+
modified: Optional[str] = None
|
|
84
|
+
contacts: Optional[List[Contact]] = None
|
|
85
|
+
salesTaxes: Optional[List[Dict[str, Union[int, str]]]] = None
|
|
86
|
+
|
|
87
|
+
@model_validator(mode="before")
|
|
88
|
+
@classmethod
|
|
89
|
+
def limit_contacts_to_three(cls, data):
|
|
90
|
+
"""Limits contacts to 3 per address. The Spire API only allows Creatig/Updating Addresses with contacts."""
|
|
91
|
+
|
|
92
|
+
if isinstance(data, dict):
|
|
93
|
+
contacts = data.get('contacts')
|
|
94
|
+
if isinstance(contacts, list) and len(contacts) > 3:
|
|
95
|
+
data['contacts'] = contacts[:3]
|
|
96
|
+
return data
|
spyre/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .client import SpireClient
|
|
2
|
+
from .inventory import InventoryClient
|
|
3
|
+
from .sales import OrdersClient, InvoiceClient, salesOrder, invoice
|
|
4
|
+
from .Models.sales_models import SalesOrder, SalesOrderItem
|
|
5
|
+
from .Models.inventory_models import InventoryItem, Vendor, UnitOfMeasure, Pricing, UPC
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"SpireClient",
|
|
9
|
+
"InventoryClient",
|
|
10
|
+
"SalesOrderClient",
|
|
11
|
+
"SalesOrder",
|
|
12
|
+
"SalesOrderItem",
|
|
13
|
+
"InventoryItem",
|
|
14
|
+
"Vendor",
|
|
15
|
+
"UnitOfMeasure",
|
|
16
|
+
"Pricing",
|
|
17
|
+
"UPC",
|
|
18
|
+
"OrdersClient",
|
|
19
|
+
"InvoiceClient",
|
|
20
|
+
"salesOrder",
|
|
21
|
+
"invoice"
|
|
22
|
+
]
|