From 906bc3b76e8f49f05e5b3163f24a4968720ce51d Mon Sep 17 00:00:00 2001 From: Richard Desmarais Date: Sat, 5 Jun 2021 17:27:00 -0400 Subject: [PATCH] All of the things! --- lightspeed_api/api/__init__.py | 16 +- .../api/__pycache__/__init__.cpython-39.pyc | Bin 5399 -> 5815 bytes .../api/__pycache__/accounts.cpython-39.pyc | Bin 620 -> 620 bytes .../api/__pycache__/customers.cpython-39.pyc | Bin 1426 -> 1365 bytes .../api/__pycache__/items.cpython-39.pyc | Bin 1834 -> 1834 bytes .../api/__pycache__/sales.cpython-39.pyc | Bin 3032 -> 3023 bytes .../api/__pycache__/tags.cpython-39.pyc | Bin 1006 -> 1006 bytes lightspeed_api/api/customers.py | 12 +- lightspeed_api/api/sales.py | 4 +- lightspeed_api/api/vendors.py | 16 ++ lightspeed_api/api/workorders.py | 40 ++++ lightspeed_api/client.py | 2 - lightspeed_api/models/__init__.py | 80 +++++--- .../__pycache__/__init__.cpython-39.pyc | Bin 8132 -> 8983 bytes .../models/__pycache__/catalog.cpython-39.pyc | Bin 606 -> 2216 bytes .../__pycache__/category.cpython-39.pyc | Bin 445 -> 806 bytes .../__pycache__/credit_account.cpython-39.pyc | Bin 456 -> 1085 bytes .../__pycache__/credit_card.cpython-39.pyc | Bin 456 -> 2145 bytes .../__pycache__/customer.cpython-39.pyc | Bin 5839 -> 5536 bytes .../__pycache__/discount.cpython-39.pyc | Bin 445 -> 810 bytes .../__pycache__/employee.cpython-39.pyc | Bin 605 -> 2091 bytes .../models/__pycache__/image.cpython-39.pyc | Bin 439 -> 688 bytes .../__pycache__/industry.cpython-39.pyc | Bin 445 -> 507 bytes .../__pycache__/inventory.cpython-39.pyc | Bin 616 -> 3040 bytes .../models/__pycache__/item.cpython-39.pyc | Bin 2919 -> 3859 bytes .../models/__pycache__/locale.cpython-39.pyc | Bin 441 -> 1277 bytes .../__pycache__/manufacturer.cpython-39.pyc | Bin 453 -> 600 bytes .../models/__pycache__/note.cpython-39.pyc | Bin 679 -> 679 bytes .../models/__pycache__/options.cpython-39.pyc | Bin 449 -> 656 bytes .../models/__pycache__/order.cpython-39.pyc | Bin 927 -> 2864 bytes .../__pycache__/payment_type.cpython-39.pyc | Bin 452 -> 689 bytes .../__pycache__/price_level.cpython-39.pyc | Bin 450 -> 614 bytes .../__pycache__/register.cpython-39.pyc | Bin 1554 -> 3244 bytes .../models/__pycache__/sale.cpython-39.pyc | Bin 3433 -> 6650 bytes .../__pycache__/serialized.cpython-39.pyc | Bin 453 -> 770 bytes .../models/__pycache__/session.cpython-39.pyc | Bin 443 -> 746 bytes .../__pycache__/shipping.cpython-39.pyc | Bin 443 -> 815 bytes .../models/__pycache__/shop.cpython-39.pyc | Bin 437 -> 1124 bytes .../models/__pycache__/tag.cpython-39.pyc | Bin 1551 -> 1605 bytes .../models/__pycache__/tax.cpython-39.pyc | Bin 761 -> 1343 bytes .../__pycache__/workorder.cpython-39.pyc | Bin 929 -> 3272 bytes lightspeed_api/models/catalog.py | 55 ++++- lightspeed_api/models/category.py | 13 +- lightspeed_api/models/contact.py | 16 ++ lightspeed_api/models/credit_account.py | 20 +- lightspeed_api/models/credit_card.py | 44 +++- lightspeed_api/models/customer.py | 37 ++-- lightspeed_api/models/discount.py | 16 +- lightspeed_api/models/employee.py | 47 ++++- lightspeed_api/models/image.py | 13 +- lightspeed_api/models/industry.py | 4 +- lightspeed_api/models/inventory.py | 69 ++++++- lightspeed_api/models/item.py | 101 ++++++---- lightspeed_api/models/locale.py | 23 ++- lightspeed_api/models/manufacturer.py | 6 +- lightspeed_api/models/options.py | 10 +- lightspeed_api/models/order.py | 56 +++++- lightspeed_api/models/payment_type.py | 8 +- lightspeed_api/models/price_level.py | 7 +- lightspeed_api/models/register.py | 58 +++++- lightspeed_api/models/sale.py | 188 +++++++++++++----- lightspeed_api/models/serialized.py | 12 +- lightspeed_api/models/session.py | 9 +- lightspeed_api/models/shipping.py | 12 +- lightspeed_api/models/shop.py | 15 +- lightspeed_api/models/tag.py | 1 + lightspeed_api/models/tax.py | 17 +- lightspeed_api/models/vendor.py | 38 ++++ lightspeed_api/models/workorder.py | 72 ++++++- lightspeed_api/utils.py | 9 + tests/__pycache__/__init__.cpython-39.pyc | Bin 159 -> 159 bytes .../conftest.cpython-39-pytest-6.2.3.pyc | Bin 743 -> 743 bytes ...test_customers.cpython-39-pytest-6.2.3.pyc | Bin 24791 -> 25709 bytes .../test_search.cpython-39-pytest-6.2.3.pyc | Bin 2807 -> 2807 bytes .../test_tags.cpython-39-pytest-6.2.3.pyc | Bin 1087 -> 1087 bytes tests/test_customers.py | 37 ++-- tests/test_tags.py | 2 +- tests/test_vendors.py | 53 +++++ tests/test_workorders.py | 182 +++++++++++++++++ 79 files changed, 1212 insertions(+), 208 deletions(-) create mode 100644 lightspeed_api/api/vendors.py create mode 100644 lightspeed_api/api/workorders.py create mode 100644 lightspeed_api/models/contact.py create mode 100644 lightspeed_api/models/vendor.py create mode 100644 tests/test_vendors.py create mode 100644 tests/test_workorders.py diff --git a/lightspeed_api/api/__init__.py b/lightspeed_api/api/__init__.py index 11bcaf9..92125b9 100644 --- a/lightspeed_api/api/__init__.py +++ b/lightspeed_api/api/__init__.py @@ -40,6 +40,8 @@ def _get_wrapper(self, url, raw=False, preload_relations=[], object_class=None, if raw: return data[data_fieldname] + if not data: + return None return self._unwrap_object_to_cls(data_object_class, data[data_fieldname]) def _create_forever_list(self, data, objcls, retrieval_func, search, preload_relations): @@ -79,6 +81,8 @@ def __init__(self, client): self.tags_api = TagsAPI(self) self.customers_api = CustomersAPI(self) self.registers_api = RegistersAPI(self) + self.workorders_api = WorkordersAPI(self) + self.vendors_api = VendorsAPI(self) self.account_id = self.account.get_account_id() @@ -126,6 +130,14 @@ def employees(self): @property def orders(self): return None + + @property + def vendors(self): + return self.vendors_api + + @property + def workorders(self): + return self.workorders_api class LazyLookupList: @@ -157,4 +169,6 @@ def __len__(self): from .accounts import AccountsAPI from .tags import TagsAPI from .customers import CustomersAPI -from .registers import RegistersAPI \ No newline at end of file +from .registers import RegistersAPI +from .workorders import WorkordersAPI +from .vendors import VendorsAPI \ No newline at end of file diff --git a/lightspeed_api/api/__pycache__/__init__.cpython-39.pyc b/lightspeed_api/api/__pycache__/__init__.cpython-39.pyc index d6a5f80193781ecb10a20e5197193d93a05f8573..36b376ddeab8deb64afc87e5990bc366614e51d4 100644 GIT binary patch delta 1028 zcmZ{iOH30{6o%(OTc!gu(`m;7r9g-QQy;t%Q8A!c5e&7wlt)c0g(;-i2i_?dNW}z= z3l}Om3tak&3GR#wH6*NcWn8$hiEcFPG%@a6c~5I7i#n5h^UuBK{`Z`7@5aH`N0f*x zO9FqocebYfxYrdgJYPR_uU0a&xGSNDbPx>5aK`r~+#xsDFe><5FSipGn8w4_pryuNH9Xxp1R$XcV%PZ6N4WI?b#Z7P`*z4g1HrREj!w!3&=Cw!8#{wU98N#Tr2|=HZ$EAmD@_33} zO)(?N%$}k1S>^ifZ#c3L%SvO>q>;+cl`oOT_tJpY&+9gIsvc}s2_(RV@+~-rU&{Mn z5*sUSKp3A^Oj&z*#59U>uqMD2bMK;(tE| zk{luo6GjN5go}hrgfV>Q@XCE$n&d5lsNpY16RO%r+XMwn;*Qn;4^VXewoTH&1cGZ2 zHgMQAogWC`TUQM{!B4Kv{Z}X=OeoxwoTAIZS~>1xyY5$k^>FP2=BH03F6m9fxx|lA zT##^rBP2#SmEw8@UrSgxRxq;j=%k;;{Jiqsf^eDkIG@w49KrDlmGj#A~c(yvri Hz~1*SugdB9 delta 581 zcmXv~O=}ZT6rF3G$z+oGNHdd8V_XO-q*5XXDisyAHCk&@5`%S1<6;Rf#BY>ATN-}5Dg7WCMO)sA8G*<;7rLoodV=1d zNmQy{dOLPuBEgciXxg|H8#~*cSn`yM%W_S&Ctu#4RqyDg((qPXujL1;9uDf$5xE-$eF*RLvHy~V8)3_Dx9QrDGnDolsJ?* zOk=~c%|k4mU^^UPAd{>sk_>O~r&QsZDr)IHbRT!qzoaTBDrlQ!TEi!EzE4Cy6OTF0 zlIuCnGnB0{X@)B=v++5)hu78_O5?A!KM{F4!fTT#WbAC@>=-{srjGM0@_3T94&hNo Sw@t>$JB$JC1PeP!+wl*u;gI_P diff --git a/lightspeed_api/api/__pycache__/accounts.cpython-39.pyc b/lightspeed_api/api/__pycache__/accounts.cpython-39.pyc index 15e1bfa244a19ee2daf6efabc9616173f08c72a3..e0c82237850de552a6f23d544124a634e4001811 100644 GIT binary patch delta 20 acmaFE@`i;wk(ZZ?0SLsW^lju$WC8#-Y6Qsu delta 20 acmaFE@`i;wk(ZZ?0SMID3O8~mG64WG^#m>e diff --git a/lightspeed_api/api/__pycache__/customers.cpython-39.pyc b/lightspeed_api/api/__pycache__/customers.cpython-39.pyc index 2562f1132ada5117448c0549e6ca5b082864c3d4..afeaeba1c080875c98275618b1c213aba40ee40c 100644 GIT binary patch delta 310 zcmbQleU*zhk(ZZ?0SH3xY)(j-$Xm}CGO^cODV-sOEsZIKGli>#F^U7q<4)lL@;Fm? zgBdjWHr|tDlwZkui={XVkJY7&}2ELzR9VKhLi6y<%kvW zgQSGugfNhGOLFphX35Euna!kFfLs`01u{U`8HkHzCf75HPyWU1Ac$eY0khnOAkOUD3%fT$+$s(+pjC_;rShcj!r9ftJ X*yQG?l;)(`F#&nSAP4d=axen`4);AZ delta 361 zcmcc0HHn)yk(ZZ?0SNjFdlSkg^42r@Ozbt6O5sc4OyO!_jN(Y)PvK7C0kSw#c!L=< z1vWmDWK`E=y~R?Tnv)jAHxu?4yOPB diff --git a/lightspeed_api/api/__pycache__/sales.cpython-39.pyc b/lightspeed_api/api/__pycache__/sales.cpython-39.pyc index 5ecb7461c4e9f5f43173cb4429091c71b35a499d..00638c05aa66f6766d2bc8a3061c330e1686f601 100644 GIT binary patch delta 232 zcmca1eqNk6k(ZZ?0SHu1uTI!Lk++_cBRDZ9)h9DAb>qxd7SBDT*FVX>tYJ&&`5CO7Flck6kNJVjhRC$)9=6bpmiGd__CNJQc cASw%Ds)7hD5TOeq^d`G<+cPRnZsiUJ0KBX)82|tP delta 208 zcmX>venXr$k(ZZ?0SNwmsY=*Ck++_gCpa-D)h9DA)w3iucjMGn7Oq self.token_expire_time: - s = requests.Session() try: diff --git a/lightspeed_api/models/__init__.py b/lightspeed_api/models/__init__.py index e7c43f3..36f8075 100644 --- a/lightspeed_api/models/__init__.py +++ b/lightspeed_api/models/__init__.py @@ -6,6 +6,14 @@ # Lazy-loaded API_MODULES = None +API_MODELS = None + + +def _get_model_class(class_name): + global API_MODELS + if not API_MODELS: + API_MODELS = __import__('lightspeed_api').models + return getattr(API_MODELS, class_name) class BaseObject: @@ -27,10 +35,14 @@ def __init__(self, obj=None, api=None): if _id == 0: data = None else: - # TODO: figure out how this is going to work, want it to replace the value with the object when done. data = LazyLookupObject(_id, api, _class, self, term) else: - data = convert_to_type(ls_info['type'], obj[ls_info['ls_field']]) + obj_data = BaseObject._parse_field_parts(ls_info['ls_field'], obj, True) + if obj_data is None: + raise KeyError() # triggers the relationships part below + if 'ls_secondary_field' in ls_info: + obj_data = obj_data[ls_info['ls_secondary_field']] + data = convert_to_type(ls_info['type'], obj_data) if 'convert_class' in ls_info: data = ls_info['convert_class'](data) @@ -45,10 +57,10 @@ def __init__(self, obj=None, api=None): _data_list.append(ls_info['convert_class'](d)) data_list = _data_list - if data_list: - setattr(self, term, data_list) - else: + if not data_list and "relationships" in ls_info: setattr(self, term, LazyLookupAttributes(self.id, api, self._get_function, ls_info)) + else: + setattr(self, term, data_list) except KeyError as ex: if not ('optional' in ls_info and ls_info['optional']): raise ex @@ -64,19 +76,19 @@ def __init__(self, obj=None, api=None): # Only dealing with combined fields now if "combine" not in ls_info: continue - + values = [] for attr in ls_info['combine']: values.append(getattr(self, attr)) - + setattr(self, term, " ".join(values)) - + # Run the "cleanup" function if specified. # Used to make some last-minute queries/adjustments if needed. _cleanup = getattr(self, 'cleanup', None) if _cleanup and callable(_cleanup): _cleanup() - + else: for term in self._object_attributes: ls_info = self._object_attributes[term] @@ -84,36 +96,43 @@ def __init__(self, obj=None, api=None): if "multifield" in ls_info and ls_info['multifield']: data = [] - + data = ls_info.get('default', data) setattr(self, term, data) @staticmethod - def _parse_multifield(ls_info, obj, api): - data_list = [] - parts = ls_info['ls_field'].split('.') + def _parse_field_parts(ls_field, obj, notlist=False): + parts = ls_field.split('.') downstream_obj = obj for p in parts: # This happens when there is no data - a list/dict becomes an empty string. # If so, we'll exit out and assume there is no data. if type(downstream_obj) == str or p not in downstream_obj: - downstream_obj = [] + downstream_obj = [] if not notlist else None break downstream_obj = downstream_obj[p] + return downstream_obj + + @staticmethod + def _parse_multifield(ls_info, obj, api): + data_list = [] + downstream_obj = BaseObject._parse_field_parts(ls_info['ls_field'], obj) + second_field = None if 'ls_secondary_field' in ls_info: second_field = ls_info['ls_secondary_field'] - + # Only a single element in the "list" if type(downstream_obj) == dict: downstream_obj = [downstream_obj] - + for item in downstream_obj: if second_field: data = convert_to_type(ls_info['type'], item[second_field]) - elif issubclass(ls_info['type'], BaseObject): - data = ls_info['type'](item, api) + elif type(ls_info['type']) == str: # Means it's an object/class + _class = _get_model_class(ls_info['type']) + data = _class(item, api) else: raise Exception("Unexpected combination - multifield item, no ls_secondary_field or typecasting") data_list.append(data) @@ -129,7 +148,7 @@ def json(self, dump=True): if "combine" in ls_info: continue - field = ls_info['ls_field'] + field = ls_info.get('ls_field', ls_info.get('ls_field_id', None)) _obj = obj if len(field.split(".")) > 1: fields = field.split(".") @@ -178,24 +197,29 @@ def set_api(self, api): self.api = api def save(self): - print(self.json()) if self.id: - url = getattr(self, '_update_url') + url = getattr(self, '_update_url', None) if url: self.api.request('PUT', url % self.id, self.json()) else: - raise Exception('No _update_url attribute associated to this class - failing!') + raise Exception("Unable to save changes of this object type - API doesn't handle it.") else: - url = getattr(self, '_create_url') + url = getattr(self, '_create_url', None) if url: response = self.api.request('POST', url, self.json()) else: - raise Exception('No _create_url attribute associated to this class - failing!') + raise Exception("Unable to create objects of this type - API doesn't handle it.") + + if not response: + raise Exception("No object returned from Lightspeed - invalid object creation attempted.") response.pop('@attributes') key = list(response.keys())[0] self.id = response[key][self._object_attributes['id']['ls_field']] + def delete(self): + raise Exception("Cannot delete this object type - API doesn't handle it.") + def __eq__(self, item): if type(item) in [type(self), LazyLookupObject]: if 'id' not in self._object_attributes or not getattr(item, 'id', None): @@ -209,7 +233,7 @@ class LazyLookupObject: def __init__(self, id, client, class_obj, parent_obj, parent_field): self.id = id self._client = client - self._class_obj = class_obj + self._class_obj = _get_model_class(class_obj) self._parent_obj = parent_obj self._parent_field = parent_field self._was_loaded = False @@ -237,7 +261,7 @@ def __getattr__(self, attr): if attr != '_was_loaded': if '_was_loaded' in self.__dict__ and not self.__dict__['_was_loaded']: self._load() - return getattr(self._parent_obj, attr) + return getattr(getattr(self._parent_obj, self._parent_field), attr) return self.__dict__[attr] @@ -245,7 +269,7 @@ def __setattr__(self, attr, val): if attr != '_was_loaded': if '_was_loaded' in self.__dict__ and not self.__dict__['_was_loaded']: self._load() - return setattr(self._parent_obj, attr, val) + return setattr(getattr(self._parent_obj, self._parent_field), attr, val) return super().__setattr__(attr, val) @@ -333,6 +357,7 @@ def __eq__(self, item): # Done after declaring the above class from .catalog import * from .category import * +from .contact import * from .credit_account import * from .credit_card import * from .customer import * @@ -357,4 +382,5 @@ def __eq__(self, item): from .shop import * from .tag import * from .tax import * +from .vendor import * from .workorder import * diff --git a/lightspeed_api/models/__pycache__/__init__.cpython-39.pyc b/lightspeed_api/models/__pycache__/__init__.cpython-39.pyc index 2bec54933a1fbbce223cb5a0f7fc8f74474a9989..8b299e1288ddc7fcefa398c9d76585e77ab1488a 100644 GIT binary patch literal 8983 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 literal 8132 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 IEI#%B4~mpWb^rhX diff --git a/lightspeed_api/models/__pycache__/catalog.cpython-39.pyc b/lightspeed_api/models/__pycache__/catalog.cpython-39.pyc index 1972dc7401bda9c8eefb1fb004e6c17a16174aff..85b5964695c0e1842e54fe77b9e791f0c416fb77 100644 GIT binary patch literal 2216 zcmbVNOK%%D5ZT1mG2h#lu$Crzsq2vDh~peWqhKx?3NoTl!95U{0|w8eBED#Q~DQp=%uIpg#tx8q-c9>=adyRAHU&iI5QmLZZ~$|d-mt=KmE~moWC`x ze*usk_=`gj#363xh{4YFbRrp22Uw=CYWc-!I~i+3&Fvv}WNlRPl! zlMf6=WScxB+b1r0G!JNtd`KP}_R%~bAJ4Z@O!}xzoQ0?!T zD5B1(xkU$6KBio?2^A@4Q_0FgMKFn;lzgT_lYA)VR6;%+6-=U)3gq+S?#aB2>N zHfm(=!YQbgmRZRUbUld#drcQD%+m=wr35wgmM_YQxrXMl^aI|4bmqEFtM*w*N*GaH zPBW}MEhcO#elXX;j#hDAWs;fdWoGh+FulKR?jY(eVwMypXna#Ie^ZMs5GdA>Nz$?^ zBnov~aDD61vJOl_m@r+*r&kq&tBN7Nt><-36c!7@EK8?4SQm7$=HPPgIbHM)FZT}7 zT0L}Ur@5o|Tro9yu?N&kh#kKRdVbFsgfmHb0m+#_6#0GK@j!RlO z38M;h2vM7fc#nlIk(ZZ?0SGS1wIs+gPUMp@GXU~Z7*ZH>7;+h-7#SJd8B&;1m|GZ9m{S>> znWLD1e3leeAfFY?XHH=YX3%7xI9+A329uVRpmSnLVorW~SZZEMevxNMYVIwj%#>Ta z$)&|5`MGYHsW~a0E-RUCv6NI6q~7AlDUMGAiTSN$ogBibB_kA{pOlrFToRvHQc{$e zR9cc+ypo}aALyha{>e)iBgL74Oco$61`!-gT>pzeBFPg4B_|s)Db}+=>@5Px7O{c| zHXz|w#0g?TjDfQRKrBHZ0SW+wc_0x#O_o~>MNA+CAmhQRuxY==VUwGmQks)$2U1(i L!VV;Pm;~4WpwLSu diff --git a/lightspeed_api/models/__pycache__/category.cpython-39.pyc b/lightspeed_api/models/__pycache__/category.cpython-39.pyc index 4a4ee0dacf8fbea8c374f6b8b4f8f242f65a82c7..1f0c97cd6f929a19eba7f6d1e11b3beb5d35bdc4 100644 GIT binary patch literal 806 zcmZ8f&5qMB5O#jrI%&EqExbe}!~-CNR%|82rBv*4u_AfB9k&f~nqa$!p5e?B>;u3f z@G8D?+8Z|{PRt~!SmDSsW6zhF@3XCDvz+1h@$KvFub8o)w0JyHTwLOG&(Hva$gr9T z7BPq+F;N|hIAU)Yq%iuzV5H;EiAZ34&E}H_{DaM-z(INKt6D!q{YL&;IsNYLqh9+p zVIGeuE-rDpTQr)92$+a<0ujXbb)1d?K~Dp?HKvn37RlH6_FK$nbk)5s@c0;)jE;w8_)~g`kg2?2V+*s@0Qew8@=&M zOp44_+g%sVvk+HcITjT~&jci~5En){FY5*>WIODg_hjTrmdL4>1ZoyCY9?FkCNklnI)mHy6k%Km_JSlqK9_<@SzwkPbhr19S|3q?z+d({UlH07wup=5FKcb$$N&HU delta 272 zcmXX=u}T9$5S^X9$PuzvScXF^L^!`81VK@3uCZH-^O9M^g*#4m_lnf!2SN%P>wq8O zUs&zW=xlUg9`DV($IRj6OJ=oFW8nHe+djX;F3T^^SrL$Ep%YpNxCcU#TOet~o770j zBNU@uckz_4^P1aD84PGaz-wV-418cCbn<5o=}qm;s<)ef4F$CvobS84#b=6CpmBnbE<5hlI^^3Jx`l|NcHjBX<_X$?v|emGmkbGVS>MNM?CTX diff --git a/lightspeed_api/models/__pycache__/credit_account.cpython-39.pyc b/lightspeed_api/models/__pycache__/credit_account.cpython-39.pyc index 24523bd62a9dde0d6e8f6bf0d75a48b8a766ca51..48378060e4e59ec818841db4e1127bbf404b3980 100644 GIT binary patch literal 1085 zcmZWo&2AGh5cd8g+3Y552^0_q1P2b0P`D$6T2cx1QYs2UvLYF0$4Ojyf3Ur+ngbWO z^8$GR9)=h2l~Z4U11IdwhPL3<&iI>|ozF9#rTu=4?D`RX`*;>2^wWXs%i7SMebrMd z0D&h^hA{FF_z(y$^Re%t*9byreMQh>{+9p;&^|%K&ZYeV4LuV9)lBn@U3zOn{E8~} zW_rp}ow}Iot7pNUebr|x7{VSP?6Uwo@K-(yA!u~i=vJ*;&~9|cX^5Z;F?8o1B&!g5 z(1$J9wrFP++FZBl*ftF6*xRcZci;|=U>A4cE{?5E;GV7EzUhx81FrYew5W28x6|wJ z@L=ehP+yeHL_(1nX97%^(~OxgEr6K>m`WutH7{};&G}4^sRR>InJ)Ml100PjrHhP7 z+!+_Srm4np+L&ziP_}IoWXB4VY#0a!)0a$8msKrzsc>>a1v{n}8O!y%<%17gFF?|B zDjJ&_H5)wm-`B91IID!_brChA>^s`7ySEcfv(j{?RM0$S!`S#7WaN(4b;1o0PPihj z!qiphz@|tOZ7Raa`QMW%GZ0j1k`>@MfjAzyV>2xu-srj7KRz7InI@Yo%iSCPXHV4V zR2BJhaKl{Nj)pxG5#nxy5EB!}RtdN75%Q^`qVWVg*CtR}ns8ba!n9_hpxO*bQO_u$ zT1!5yG*hNe+*xK-p1Ss%gjA)QZc<5MfgO(q;Di7ZZ0-4uB4qva!{m|yE-*FmWBH%)I{+4LaUP5#?X@g3vC%psEK8>vq|WBI7j7$Y*_*~E$X2a zksoZt48W9N@vtXuceMJpHE^LxP`lypJu delta 277 zcmXX=F;2rk5Zv2yBrD=vP@)J?M3KxJ5DEf;Xxz}bu@mnh9kS!-?p#ol4@7D60Uklm z3n;GQC$N`fr5(-e&S>{5{g&B6sWEWd)1 z@&fs2+fD0)opWw?!JwfO1iThn#=stpD3hZ(sC%o)_`9a*hm{X#D5z#|zV9B5Zt3|U z$B_8?ARFDQFs0F;v8>*JpNxc?43V6tyKLxxg>OVF}1L6(dg1F`5kVj|1 zsQn672u3v*ab|^;sd!Z@6P?eQ5l<%HiNq#2X7$;H;353X&tODAYCIry(cm>+zp9In zH!2@izUh4v_Q!rWMdhQazg_irsyOz(#k*C0v&wH(e!KEL^t{gpd$NL{3bndg}iWaBcITyO%Jiz0)T+hA#4WY#sri#dr{VP{V=wJ2LhITx;t zY|ku9p__|D$qeSlBC~q&SlC$(nZ& zkF8rk-^I&QQ^{S7Bc8HF3Jaf;?n-1vVL85&7RpM0PaWX`n_4Kpg_+5@8TDOV!eK04 zoh7(6EKCM?II5Vj^kd59R9d6kFq^ccp%snoB@J4_fE#((d>O}n5g;V>f^=aba-una zaszDUTK?cqLK8$b@Y|(1$J26(07(q9uJLs}KAM*Nwr}{+3<37n0Mchj5ZRGvUJ^2= zqqd8vA5E$2PzswBNGuUwxDG@OiLcgJ?ZQc(E7!bGIkUQs_y#ohBB9J$EhmK) z=4GJYfi7?U`sV1<@mEG@GyYN{4?aH5lLB9m89&uI>dTCkoX)J73%Dkm%iAHGN+>f< z{6|djnjg&1X`AmVbd|{Ef<`~Xya==wLRepKp}#7R;$PmVRhaZQy^WkxzF z)6axG2c471f7>7^(&6G#Cc(+|Go?2wt>g%G~>B>OP5=QpIESFQs8C3An))+<**IP@T|g zJ!F?lljN7T(*rIh(%u>h->`-%lC7mjLp_Am_4~L*T+*Rf2FdoS^g%QvE-Cq57a}It z0U0hK?LZqjp?Jh~wp#%!rMOj5qfkdDLf??X+Hw~bSoyYTSv8F2(cD4v7Mi!w+(mO; z3Ch;Hn0pTljxZmB`ETiZVgKQ0osODi*$FiM6W}H7J6^Ysiyo8(?K7-Y2KCUbbsN3C FzX8%_hd=-T delta 280 zcmXX=u}Z{15S`u4K{(uSSjr(5BE)YH5h99B8oPkRTxQhBhRE)wu(0xe;J8+>_D}Q^ zZacq3XHN&_@!rgP%zV#2Rn}-d1+LBh^7b88+5G5$6#@zay{LqMOF)Tg38+ZCs!C#V z4f$mCa@Z#9JmYqI3F%Kt!=+i1otq$mA=qoLDia}jprFTa982LJ#7 diff --git a/lightspeed_api/models/__pycache__/customer.cpython-39.pyc b/lightspeed_api/models/__pycache__/customer.cpython-39.pyc index f53a4a16b8c4561add3b83256b4253c5e5444ac6..e164f1cb6606b9fe4c4511ce269b119837a5e7e2 100644 GIT binary patch delta 2379 zcmaJ?&2Q936py{$U9Y`g+3e=~V>clQCLfR#D5XF{643@qT0st#t9Ehr4T;swZZvif z;n1jsOAkHJsnkmjy;V6C)Kk?%|ADF;da2Bv6Y8Zm5K`Y8JK02ndgR}{-^`ow`#pP} zpG^HYk##Z|qYbaC&wjggwfAARU;Vr7;pD_l91``!M$%3yZSS{167&acptJb>n5`pE zfq~qRJcZnZH1f3M2J#GKk!K}0k>?-_16wUo0xK;<56<7--nypsgv8*wrw0j2@B^OYLfQ z91J@lhSUqAfw|(`U2uH1*5unk#&40O#XXIuF)=(N?y5tBfxhT^wdPjC4-AN`rA12% zR2O(wJW?+zN%4<5EyiMFi88G#iyJXJoc4If2%)mEdm@70M}-{{d9Elw#o^86It$w)chkzzx4*Q)|KJF9iZC(&uyO1@e8addc{{!soD&e9ew`3j~$(O+_! zso@=7VlH{Vn;yTX1e(X{w_Azi74bBA);yGZ(XS8W%jkT>X-WTPhLi`%Esw2iZ?e5u zbPQ-PQ)@QvFz#1tb;t88m7k&YX>lTb!wL-7a~q!TG-{0NOp_phl`o8%z!Jv4R>OK_W1sbe~QR6SOOs0g2h?(b(=(=KBhZaRn zJLdOYq>G;=cpCwupC_B5|D;3#^YaAn5=1jUq=)w-{XKpD-0LEJkKE-!lnXBrEgwJ> z@=Rt|F~y&m+t%y0kgHp3q*W20WzU%1xX~QmURDub=^f%x_R}3GVEHXT0Sj6OmbW6Z z5_BT#Le`C~2fC521!o$Tlohk8e z?s7?1gAB?F^ya4THXBZz9}`RYcXrU3Gc3m|?gk4|KH^Q^+1Lz{3(bb_)O;(d$;-Hy z(+<3Ywu!AcYxi^nZyLeY$ckJ8V`HoCyJ0PAt;j5J2Cjd(7MAoJJ{IZKh-0Mfv^f=T zPQOQ9Bq$N&2=WMlw%qjDenbaba~Yc@{%BvBD9VD!Kcpqs38+5U`l`$Qb#S)%IE{To z@Ugg7n7odqQ_6=bNe>Em$o%R?6Da&>EQu$OG7)G#iVU)0;8iCS5*}6hYIv7;R(L*5 z1sOl|S?uhT(`YufH{5&d&<9FYHAu)x#lHQncx{A2$Ny7;2lD_$1OQ@J-+y%;?~mOhoN@OLKGOM_FZ#Hp}xMo>Nq@}ICoV|aUK#V@6i z!K@Ot{PRSoreITzgdXGMwQLo{i_$>p7Ojv6UB1|1qL%o)b9}B$W(z{S7clc%b#{qj Y^XMm>SJEoBSl-BI@+P95&tZ@EfBXmlq5uE@ delta 2689 zcmaJ@&2Jk;6!)&z_O9*qM`FiuoHXA_-F)=>yOg#`DWPpZB&aY2xlZ0RwY0X=*)@=Y z6bW+SfJ7ODgcKx>R9sN`6OcF|ap-M99Juwwg<~anZ`O&^Dk9eW_Wfqwym{}<%$s~S z_1jFUYTNM;{(Zat`JFqZhpB!&r#+mW+6+U)iPmE*riCC3QOB%X%u;g9iPsY>p=8rZ z)@^1h*>Y0#G)to#hlG=EK7KKR~acD2UFkd!&y#}INzM{XRS#nFCjVmikQ9jluqN6l> zRQ{>6QVPX1HaCf2>ZWFDDJ}IZ8Wkyd$Ii*M@F$53sdp1(0*bY#N=^fQadPb z8;7s-)5rk90fIq-Aq3x8X*L}(OnihO@L*ROBWWC=D1S9d+PGT!nK3ZU4LoGVwvHK# zfUdsL_BTdfFt;Nt3Ks5FTwaYF-c-I$prx`NB&ml?2hOA9N8YbGEnbcY3W6n9Tdvov zSMJuhgBc@LmDy>O7RxcQUp(Zd3MvTa`A8B%g))UY6Y^**uT4=XWdr#oY;GR{4>1iw zOb3mJL5J|RUelNX2F4JP_oJmH<(r@&1ckGR-tOm!%I5RS2dmHEq$Ai|TGrI#|B4xv z(A%+(dnvL(^`6McJDrSTzxS%P7i zjrZJuvXD6QoaZc2-ZM#Qj!BvLs-VusDKc^z0T0L(lF8|}^W?BN zM{u5?J@P|xWXIDd$#ZA+^Ymp>S6iVbUL@M_uI=(wdt1xMzwEo^{Y>n;k@9}(WW0pd zE@5-e$sbeAO>TlkWe_Vx4(o=6EWgX*$a;2}0R^momg2?j2v4Al^{+iMNwp99ksUxg zHVncL47OPchG7JT+bj*EFb1QF)eN42aTrHp!30js;&&1bLU~Omhv3k5fpx(Y>xOBT zgBg~GQ*aoLY-y|qj>5665G%lOIDy|@IElyov~SL-3&;hF2Un4cg?W*Yzo#$fgBpj? z`(|_9t2G-{M@-72nX{XyEI1a_Wh}_dqO!c?RqN{yN^{MIS6%gHuhy$I$DM6c#aOJ( z%0cz?mq+;QO+UvX<* zN1@IAa7#FNp*z8KIc~+}tIY;f#YVdl-Q4xZ#3#g^MPLR=B8DS3SQ_K+ygrz*JWZfl zE~uERN-ugzqUS^82rPtl>|!@waZ?`8E>9JLl5?H(ZV*r@Wadguc=w>XAYxp9#vvL3f$oy z|3Y5vzCNl7{88+FQ433y;OnAN`C9(ez4X6&_p0u_O640_&W)@T_pQrAv_P2P5P?o` zg5Wa2JOTY@rM5EIGkU>;ouW+=^m?@4O8HUlz|1?u8Ns^*o$38 zGW3Y={xVOaMMJ}%9*>$~rMp<}m$Cf7wPl(|XG*9{D@WcUN{;wp+!*C6NgoCt-l9=e zE!A;Tui=zDo1c7bAHRI_0-lPN!!J@6T-uS0meiBl?oUsKGx3a#Et)Yi=}dy!f7QrG A761SM diff --git a/lightspeed_api/models/__pycache__/discount.cpython-39.pyc b/lightspeed_api/models/__pycache__/discount.cpython-39.pyc index fc200e34dd94a8b1377a2eca62bf0fb73f9c693a..87d6988171479e6a82b4d60822e63cf190d59d54 100644 GIT binary patch literal 810 zcmZ8f&2H2%5O)4H+3YT$3UPrehm}ydBZOMI65>)S3LK(H#+#XTT{%r^J4o#nj)*7d z3-A=Y0T19Sr#=Fl7$>`Qg(J`S8;`%t=dqa0az^9p&u<@pgpB?2V0**__Gp?XWPm}S zSj`F+FbE-1K^+!hz+N$kA^E}}!SHibL@+vL^Yn&JuzBDz5EiYh@g^7o`AcE&&GHN@ zTlt*rF($A_(|jO$J!qFUBw_ za`F?HQe`ujEu^WM?%cW@)~nZxdFWz$*`mvo;U^LmxcFSuSlrp;! z8ZSDj@t`xdsZqO3=xQZDA-J&@er!eE&Qljk&|aV$iGe9^2L^G9#eHdbwI<+eLU0p` z{v+E_!dPB6AgMgyp8D$EM#|I2X7|iA=U1~$q=$#|smnO`LO6Fh_s2Ws*Hg|fI-%A( zk)+#*jMXkVQH`)JUN()=^d^|w;Z5HyF09pZ*;zDh!n@WtmUmhas5IUJK!wB3ccvc_ zLvQ}PJ9t*UH>gedR#q#ap|6i< delta 272 zcmXX=u}T9$5S`h*$PwHtQc4gD5zcQAu?Pm6YwR|~CAnE)<<67cy~5i3KuBTZ59mMm zBa5xyp|gnt^LTIOJ!U?SzGSgdY7AW8r<>&)Y>Vl|87l(97CO|3Fb5JOH$c+htJH$B zdniZS?tGE4^M>1>Fc{ECz-u%z1>S{4ogK_EpIP7Z!&8XDQdG_0L*Lz6)6(6n6fp}g z&nD)ruOHH^Do48?yqr$ze*I{gP_II8c0B~+KfozI=G4v#T}h_t37;Sr<6oC1RVgDl b!E7)814-_q1+$o6bv+HOxlT5p;8^?t;UzuO diff --git a/lightspeed_api/models/__pycache__/employee.cpython-39.pyc b/lightspeed_api/models/__pycache__/employee.cpython-39.pyc index 8ba6e2588d1e995ee0a0b7e10c3d2df3d4a7dd83..148f2f436ebc7bd6b84d6d17b237fd156993c2cb 100644 GIT binary patch literal 2091 zcmah~OLOBy5Z23*EL)C~Y{EW(T^2aF3c{VDs1z{|U$RsZE>o`3Dw^3?i)5LZ5mm_* z?)(9txaG`0;AhO0(_T43LDAi^97D2&UFw;yr!~{v(_br%E202~nZ0EfH_xJ$SL zIO09PJ;FZkpIW2%6`Wv=Y!mWS3!RtZmE9B&d_G_xAyNTXobaWZZSu= z+~&^G5gvDOh?D$crrDO*gp={LV?2Gi5GE{C@*)=nH{Lug z#mF&E&L!l?I==Qzm?Wr85)&m!S@F8Sc|S?MtJ7k&;^uR0T&1PiNh)$bNwt>wd98)I zw&V`%MdFXMqmQTGC?VDKB+q84*)yzfNaW2sUWX zKLIouOWc`qMhBmn!P=UF8MzW2G*MX>I;U*Q5EP)tvZObSFUmA8)UoWLYMr!Zwu`!Z zAO`R#e3t^XqrXUdE7T!a9HQyZAYKzIcZHjkDb@9{oqW75Wa zaN&@Cix2rW-v+B4u-fJK_#V{YF5jo|4St`-2mB$9!sm}@mp7LK7Vx)N$lqpN7$g1| z68yx3ZOTm4CU=!s(|wUkrH`Qwp<2^s#)6{CzI|3}6Q(ko<=+c#!lPR0sua=$N7Y=X znP%OrnM}qbkM4l-ZhCFeCpkB<6h*4@YOZGaLfsJOtE!fY?PqwCqh*C&gHeIx)DT{NJV0gf_3R2kvc$3X+ zl}+|AtEB{z7MBSn1FZ9TGNbf(68Ydu3Yl$DEU>PtL}75vS({UgA|?;y%6+p#5SWeB zfOo<9t6`hWkKR`YFI6?aS~oO;$D^L?V>)9L$O^fI0t?@G=T%im2k|!IUQ#cx+a|Rv z0Ay0!0B9$oS-F#Q_u+lKHuWBehI7zh;6H$8NV37<@BYxUJzL@%xA=(kKZHkp0pkDo zxN1NS@ht#OMavE}q8@`U2ymwd+=+oR1NjbYcGKD0%EV^Gf@~(RY*|PXAPPAqAHbq~ z7sXRF^YLnc9G#|2I!dP!nz@(*o5bpiVLUXt+G}rw&RJi}&cD>FyXoF}H|qLxi{l7cd)^q|tYd^;9Qkg(C42U$Eei}WEKq?HjL|V-gn47c zJiw1$LM-=LV0qARkF_pQXW`Z6L>+9R{Y=4G{~mIkTjht%ORQNLP=Xms2*cpH0#AhF z#Am)u7MSp`s6;seFoZJ`=e5#hal}Ee)9!>AUymvi9yUK_mJe#FK$wDM2OiJj3f?+K6z4on0X0*BxbUSjYh#;e$Lv~`tArUbw$;isZo37rWNR$tobs4zGUXSR~*2pV` zEw~)eG+K>T4ZTaRREvGxp-2Y}s8zoc={Rj$)oj)-+F0z|LJ^Wwr2Vbvn84+?a#Dd? zYHVvSXZC6|BJ)+}wbkmgD17a4P6~+&6zGLlNxVutpDFGh50tZKFBthMk$aTgEOb#W zTq>n^Dy7S$>;{-xKQE;GG}Y}X6RO_2&{*y6%VB;8SzB_loh+JfOh52vd-(PG#gpot zL2arxs@c~Xs`b!J9ro5#uk_%b&{VD3?X4LRLDr-CpOOb`O*PwoJYH-1k(j?CbB`bm mQhwW4eD!nAUX-C@I-vAS|JXUs9#X=kPmkaB%x^Ouir_cg9K1dN delta 218 zcmdnMx}BLXk(ZZ?0SF$-wIobon8=qF!2;wt0CBMtkVs{SVoYI(VoG65WlmvAVV=X3 z$r#0w!V=7&$$E>`GdD3k^%hfR$}QgH(&Cc*T(`{BoD@$NO{QBcC6xuKw>WZ&ZCa(WQ%s_^p lCi5+ZA|{Y5hznH*XYm19w>WHa^HWN5Qtd#>ia`Vq69Dh;H39$t diff --git a/lightspeed_api/models/__pycache__/industry.cpython-39.pyc b/lightspeed_api/models/__pycache__/industry.cpython-39.pyc index ca5253c5ffb7b50633698285670765ee44af0c0a..dcc9109c007d7faa5e9c10adbb898744c9a88292 100644 GIT binary patch delta 267 zcmXXQa9wvcuQ%TGGL&ny#~PR&~uYu0vIa;7rp8^dHPkY0*uObG{yFl3Hj^{(F4o^TmJf WJTSnFAnKcK2ff8xL75+5B>n(rn@3Rq delta 204 zcmey(yqB3Tk(ZZ?0SFr9TN0)-Oyo;*VFB_SfVfx+NTf1EF{Us?F{Lo3GN&-5FwbGi zWQ<}-VF_l?WWB}VnU_*pTvAkdizzeZ7H=|;ouBKLnVOR_@t_E^pC;?Xe_Ap^@%c$v zsmUeri6tdPnMtK3sl_W9igHq)$ diff --git a/lightspeed_api/models/__pycache__/inventory.cpython-39.pyc b/lightspeed_api/models/__pycache__/inventory.cpython-39.pyc index 23565c653b50fb92839a96f7f5a7c1cd2f32c7e8..cb09244c4170a0d6ead11900df6b3568b7d406a3 100644 GIT binary patch literal 3040 zcmbtW&2QsG6nC7&aT3S*=(f9E77!pl!bcI}wnC^hMJSgpY<3b;X=fSVdSfIZp*+|sxP*r#p4ZH-;pIkx)U=U{==FDaLXlF4|&o|jBR;~*65 z(aAFw$q2{X{M7*L!y_I-!7Ngu7O`2GmZ-h3S%sGMy9)Lrc4!sfXgzh|U0+m5jW%HS zrt*jB1xsbd=YwpPO4W&1@bK`;Ruws)GUX;BIE`6Cl^gPC9A7Z1Drq=js*YwK%WyJP zmE&;!I9ub5TAa7==9rLIHXEj;8tJtR^2+3 zY(nlVP1LT&w&5X1DRDHDl+UHHoTKR+&W)wquP4(an=_^wEUyg@RR!lNC)O+U9p1(9 zTeyY`4oPJ!=c>NkO}WKXRlzAoR}SCC89P|q#o`_o=rH~o7EAw_+UwZ%1{585gb&3H zN=xnw-S_`Ug|qY08G*BmXQio0^?=lt+Fd1e5GK(q31yt6FLR4sE8Ny1X!$N1lSYKc zj8Uj-EnIWW-@?ztuUlc%XFpCsg`hn>lXzhyLG%XkcW}wu4u0aDPJP5ng~lM`*zPL8^?N4MymCEBJt z^lpysEj&mNyCC}es=IXE6CS4GlyTLNd99E*CR-gtcpqwz6*9>n3CK{+1aSmJ(?=1Dycu2Yf?PM5HG+yqmHSGOjdq#M&MfpQ0UgutVykM||W7j{C{n;7%dkB8qDA(8^%h;^m(aV8SH+(ngIT zf!?a*UEEy<-TPSE7IBX^I>a^yU^#4ze~eln$x~Yq zh2d?4U4a{mxzVf$8-(A`0@w0}C=K^2+Jbjy(FEakLAX7&y(V1DTEt(9S7$Gs|A-?q zbT+BW%=i!Q9^et;8Y&rdA^tv0W{iN~(B+J3sA^F*!5f1)0muUX1PeUVCaAv+wSH4e zfV51w$sI=81};5D{Ak8Gtd`6JZ9(rE7u%A?1=?ZyeN!*`_~*E*$;HK&ngp>y1s~yf zEdVWrjVeF|dVd|Sp+?ic2c T_a&O7W2k3)cBj#4>VM}S7zgy% literal 616 zcmbtSyH3L}6t$hCgs6p}(2dOjt41S=$v23vkGDjh1C?pJX$2NE* z0*5Yh!OSyj{rsNix{%XeQRUhY!AvWulrFLp!Bf^ho)hEhX~~VBDmu2>VD1^8(mc*M zr6!;>D_E7907(Kw5VsKvDQ+ID$Ug#$#s~khSyiO)@T%BggqfxUvV;FI93R{n9Q&hk)K zGS1bQOKGj8XQ2TwkgGObZnntUR_g%Rf8uKxs|>oXL6X=4J8fO{*XPFHUmW7KznzV7KBgsqlq;CJuV z*Z=(bl41Ohg!0dUu!=|i8^8=^CdSzC4bxy2vlDY{`IfFbi9L3F$22}RScO%e8m!8# zf7yNoa*fp?*R@=Q++Z$bSIafXP3A%Nv|NYWVr|H6EjJ){SQm0v%Pw2mH2S@7u^OXq zsyYiZp2cJSt(hy_`=R8Y?mXgAhGu&!97WW%{9A#ribr~Qha0}h4Bz55GnsX0afjJO z=@ez9D62(T(`AL#i?X51DszjnS(IK;wu-W?%aidtTCTBfQ7-AS&U)+|J3lhng+qs3 zWS7`Q)Gr^}Y?)o59_%W6iCrypE9@FuDeBkR4c-1S!ymdHyNMBgh4K~lD!VnZ^oXw= zF8dAk7VERupPIhQ-e7M&HT)*K4O$1v*~s`{7IGOE+Q8p7?P_HKAyWuR!)>4`IwQzq#6=i4PV^t4D zv={Gl=6fubQ97Gs;ZDMpE4hfnBtAe(YdYIWV!5}Kei)@=8cjwbo@Q}6Q4O;DW)_a8 z%8dkvBWyvjbx?=VrOwthXe{|5Z!OyY0nOsp@MO zZIp^97`do{4qLyhUl2Ge7>3yTB+NwoSb4-*Uf)<(-X|p)X2f2MlCIB z7$q;1unZ)QKYlQV%?RI4CBJChtn7M3voQJn(l8XkEb z@Rjk%cw|1ZP}*Oa%>0jqQ)dkksR|EfL74R&Wrx#PRb-fOsd^)x1-csSrXrxFiay5b z<8(@%?230F92w$$6we;4y}SKquqC%YkE6X%uvrX`aFKi8@cy!&TEL#z|oLN?yfWQt#9}SSIHioYgC$m^}YcogU3a6&2zF zyEkd~&LkZ3zA4^=UT-9a4~5C}dML2w(=eA1SlblyUPc+`=G2?FH?Wd0evBT!xt zd^rn~qQ#CUh(0nCsTdz|YQ9Hp??r1&KG$XgX7| z#$nAzmICKZ)gk^$2Yv3wI(%%bi#jG0G<6;??HPh^m+>ebhsgthQX-^^E;^`MaV+{Z z?UtptDzizPNwrkG7fg>|e3M2V0)|I5ai0Wo3$ae{A;Cuke<0W(IE#1Ud>NG3XF?yh=&+`2w| zZNPIRKGSc?(_A6jANF?CVKQMB{w8?keLhOC|`)bKs6Mf(O74B)!)!SZYU|ml?eGk zP|Gi*1O8Q;{Y{8(7UrBzr7&eEz{yR zkKYaMl!dp;!rfuNdZt_bv`76NG||aEO(3aD8^CL=r zMfz%S8L#2ng8a?JH-(EUu!n0iz9v|!sN+k5b&C2DF2>Xsmu+^g&|T0M?#qytado}| zi7wkKxH^-3?Xct5*>#xfhPaC5ikAo|s0cbfqC-H@NGuUF3Ft;5Xp`bH!7{-Wf)#>m z1lI{}5WGxqlYqDq{~)0BvUQ+unsPus;#UZzxInFRe#I?<*9dL{`knj$yiSTY2z0ci zPJnN6jJ;ajl+K9Opo}O-ad&o7KtwmImFyw@yHv1 z(g`c_CgsIEKO#4Nr+FUsn>s@)L(pxCz8*$8TWfN3sLzR@BbrmE&GSBqCy?JJ-pg+W VAJ8Io>d+I~-CJF++w8hs_ea+jy~O|k literal 2919 zcmbtWNpBoQ6t3!J+|#po?5s|Lvys5Cm_y!hw^UUpjYi1u`D=dfSJPzd4^oak z9xC^+sV7K)0nb^VMT|3Wz|DEzi5x3?x!d<5kF)O?_)ytnP!Y~kH}X-Rf&lfv>J`+h z5TYJheG2s&)KRZnJwUwyP1KuKufp^)YqwtD*IApJ01_>9))z1M*bsh`DDl(Urbsn* zyDLdArJ+ZkIaKapQx%*eSj2%vj&Om4v+oEG+>(1G_e)+W`IO~81SPLpUV*UWwUXCM z-Y9v~^27C~tv&^)Gn_B}*()>>USSMu{EpDXzV;&5^QTvUad@D|MP z@hB8ExC~eJ*i#j%mW%aAS`}({Kp``mXVowCI~khIFZO;22MD z4}}SG6|ZL^hp4Ky74|JZ8X0#qOpPm&K~zmgN*82|ru)fYw4S7TB!whH4a9mf%JpKQ zbTpN0^++(TPM(+`k?BUZCBW1nQ)w|8=wvMyCR9RZNuKQph#JGuTArzmmEzm9=o?&3 zr7|1ptQeRoWqesD{hE8hGcS@NQ>5M zBme?pUBm)IWc`yf*kNW7mX7-ckfS&NgJbIBK$V(@K~hm|;%tmyo8)cWx9*aDETLqm zBZxR9k4c-Ez}>Pwqp6#^`0>d@vV(0AoB9;VE|&|t9QYjDWie{tSc#+LTFL2AkGso} zdcd$My{??Xner47@~rL4v&be{hd9y38zg-pX;VkFO;rryV!ZoZ<0+A^8y9usCd2H2 z$;&wE@B53lR)5AyQ>zcMbR&_l+9}deUktQby)O$oI@D^O^)|E`3Jf1keO1NKid?Ny z#1@9zCWzx~km)$SiR&xMHRGNWa(XL#r?EuiCSy`MsU{D}6e$~}c@%cpCR68;@=fQN zi`;uo@v!z~cEG!B-}o_QPttJX69yH{^n|w7c)gG@*=(Q=xV(g;tlwjDg;WaGC)iX4 z33DoZmhV(8NLo0hv`roYM>((n-$TJB3DehMzg+muA-K=Dc6J@WyI|jW%Aa%29)~u4 zPPgsIi#W$%{q}HIxbiw_m1H=?qLDr-XtM|jlHvL!q@QEgOQZom29Kc9m1NIzk{^Ucg>F1;$ZRNiLL`rKnl)c-RoO1w~U{L1a$T9NYs%n&C+Sc{|B=^psX}!dE^ZWADlB06 z1ui{?DZeD~BtW;Y;|FYqfXMO&fLa9T>q$W@3;p_7zQQlxKZb=YZ-6BvEaQ9n^-DC&^iV;M2%(BraH-O&EtgfKW#iqrMr%9lu2I94{)`+r z(!b;@C$1ckkeIQZG=*X-GyBcV&gPq)G3#~%3!aOoU*CW8EbBXh^``;g5Ptd@2ySs( zT8T|;+j?tphr2fxcZL1MB@W;Q_W*k(c6sx{8n%Aeux)4;!E2_)>Ewe5O$e5I4;)(^ zu?Gub5u01Y5iYm6vq0^Zx&i3{b8q1iuS#C*9OX)hG@KucG)v-?nK(;}M#>U#>l6($ zpNYbknoeUOdEtFxG8d$+<0y@%amZ3LbQKa*J_yo_HC^+KhRwpK6q8X}1e7Kj&n2!q zlzz;atUazas%T8IOcn#0l@uv6M#Yod2z_g*KAwQ$*ZI+l@jES)9-qhI6;phCoP~KJ zQlrObD#PmNv5ccDqh~^J%4YH35dMUa5R`>Xiv8JKxsVt+Xyo8Kz76m1()v%KHzDE> zLvKL*x6p1?Xehi_UqG|IfL5*jQhR)(_6N0Y*Sdq6cX^NZ`R2lfOk1^QPy=+^Q2@_T?2MP(F(IXHELcowP8>}(hKWbzL+PIOp?wLA3-h1))Jj55yl!Cfb51$ zU!6l19;eZrc@U?e%(=K^*Qe$21RTzLlc|Im(!=#-oYc51=;i0V zhZ;`rKV*g0Y9gH=$)$mIgAPk%~7^VRBL*sJ&tz^}G(SAWTh>WXxCWeAb= zUx0p-r8bZTepXZ)MF$CaQ<*WNx@hR3SWQK>!roL{ATU}#0a5od_Vc|rK=iYW g9Ss|Wr5xOfV^G>2N?iCatKofxu^Pvv<81nW062GG6#xJL delta 220 zcmey%xs#bMk(ZZ?0SG?JwIobsn8+u?s4!7mo-KtXm_d_uVuDH}3sBMlh>N9wL@GlR zV+unQQwn1$a|%-m^Bks3#wZqu;#+J!`N@enskfLiQ*QAlmll`g=elL4=A?MKXfoYm zDXA<-y~UAJ9G?af^V4LVEXSzEoS9cL*_TnAjTdNm5%1(I#za?;g&a&=|BIM`3_nfg kTMR`^AXyN%2%-+o;sdg7aoFVMrqz0Q^i~TXF^mtbU&j0tyQ~G#~&a zW>)mFkpf-==2(0KEJ)sFO@@mzsF(MA18NaV)PaKO>7END$LdgfdV79FUFcHIwk#MN zu=?k02pWOVNXk&4ypxn;Hthx1OSr%y?YOwhn*}bpY8k5&y&f-g7e+@e?)6*f&2cSb z9gKe=j8OR+DxrbIs|9V$3XrExW^ot%nO}o!g`?`qr8+m%$Ij#PnePhfIX5Z?{|VY`As>*GTHq p2$ZrE)A}>{PwRZ92QH1kpnE6&;GH{h8zJrg;j>ruD%Z8%0M^x54>wxa>?%*B6C| z%D+6DnDwq+rde4`Hn(^(->b*PqiKEJcyGH!@Wy?D)qK$E%?I9>l4*LxCCJ3|$EHbH d$Ou+2+sXexlDlZmEM}*}NTD}p$>tKw#4m2dKXd>9 diff --git a/lightspeed_api/models/__pycache__/note.cpython-39.pyc b/lightspeed_api/models/__pycache__/note.cpython-39.pyc index 54e9c7e3bd27dd2c053bb53a7a8337899b8190ac..858f93c16896dfd029acef6aa428e03bb14b06dc 100644 GIT binary patch delta 20 acmZ3^x}23ek(ZZ?0SLsW^ljvx!vp{<#squ- delta 20 acmZ3^x}23ek(ZZ?0SMx*wQl5|!vp{@76kkN diff --git a/lightspeed_api/models/__pycache__/options.cpython-39.pyc b/lightspeed_api/models/__pycache__/options.cpython-39.pyc index a227109166612286a7c36391c3850ca91e8977f4..d9f829d1802448075ed816b77c615b98ea4bac19 100644 GIT binary patch delta 382 zcmY+Ay-LJD5Xbi`oApR?R#pdsg&0Ke5wx;XL2aU-A-h5#mpdkjppAt-fI%#TD|{6n z!FJ!pkC~{&EzEy*cK$p3cHiYwkmPYZVz^$n-mWfqpG^2}zrDV8z^t;tBeQ|y4>FTh zpRhFSk9W>D(Ck>oEz5+HmRqr)EGb*H5EulD+A-j=i4t2&>$RCv|i&Yd?^b9r65 z%KG3YuR3gi!if-~^dG5gTIfIT?gK0vjbeo1<_jcl3yh^jrag>ig7VKqL_~uuvgAnq zOB3%G#jLB_KPXY!VdiNnfyVgJw5!^3AOp?L7&}ZA80^3b3H=d7B=NE&RE|-o+0avr Zh`xstoZuMX5+&1ioTN+Eu0l}*X@LR_($hG~ zw)g%49eVY-|Dr!(u07?@Yk{VhzBeQlS+XVY4Zk=0_q}<;u;1@$`1|9|??3*wp=o~+ zSpHcE_V5u28qh#bv{dsoT>}HmL{AOh(6w(gu)zLR1DhGYn7)a)1#QG_g)PJmbP#tG zwh?!shq$M33$Y7*#C?U^FgVbL8!vH2ZKz8J!h#iX%3kV~qkBIT?7`7bEGi;uv;4b) zU=JVhCmN>tI%vMZOwhrY8_WW;F0Hz>RcYgTsosWmT{^04L8mUes%%5AF5TMSugifd zP1v9kJp6BhccR1A+=6ZVUxMu#?Z6IPhMgK+fvfNaya{ie81Oc>y@vQY+`w8wH|N{F z1GkWk+tPl(fpOW+sThw=4QUn6XG}VY2##ZxfZtP93`WS8?{XHg_*sp5A?NY4Fi}-4 z&WU&}u&&HW3co`LOo(X?b6nXUYjLV@$`?)CmPF_qISFcW#r?Z3=ID5#WDNTbi z#Ujeftnm9qUf@(`;iEFlinw?#TTfU9Irp7ssvM7o7I#oA-a#XK&2EAiWS_GnR4cxGjwsd^aaK83YvIK5n=Y zt%7c?T7^864Vz{W3$ijjV%*;$ZDw(H62!|YZYdSd`UZ=p5o#$gR!GdC2C=}mTG1YY zMpWNxL=9$)M-0UsC~a4+i)4}($s_xb(xnPhA?*Yi5|d#^IzfGNE@j2ynQht})}R;0DoaVx&s@ILX68T1R=F$gLn(X&?xTg2zW?!NjS?-=kw&JJFOy zPZ^9ezlWjsxDHZjt5}d7irs@EOlNAJoai|d!4lyt;WV2-ODzx;}A0#GNiQl6rEKs zyUgN3$j!PRD$;bMo0WftW^#HyueYedJ7g~8^$ym4jF0fptnm6Tr`k0}>1x=+N8CrV z$|xQY{P0j0`}r5RU0vYNowtz59y00jEgVR0RVnH9RZ%mj7QsKkCj296hfL8u2YiX5 z>LzF@>ZH4z+hiA$=UNYb4@bHv8WmxRx(ivO1%Hl@piAZqYp>L1);Oa>y+#*P*=> zcvb6$x$r1q{0nS7;k4;<&*xWIF~Ubs3SDC4KTb81p;KKL8n+NuNr{ehB`(pa+MPGF ZbnaF6(3b`_Q{%h3YkF6`4bSy@{{tI`?AibT literal 927 zcmbtT!A{&T5Vf6T$)>DWsKlWcuH8#Nph6X55fu_zXi+awBsXMS)>X1uZ0EokKBGUd zzvL??et}CnV=5L<4@K)JPxGFYnKxsn91Jo>Yv<vUUp^ucPG*_=eJ}VtjW?jqt831?1GsMv=iX| z%lKpQ%_?JyuezF+28x5W>KZk3wm3B{b!CfMUre2yD+RKg>z5SEtJ)SVHSl46Wg<*Y zja!0LWWR&5!fg6sHphCV45c&)K@VYK`V>BP^AHV5+eD!2u{1%-3MPiZ#CUl$|LKchhCO=z0B4oDGynhq diff --git a/lightspeed_api/models/__pycache__/payment_type.cpython-39.pyc b/lightspeed_api/models/__pycache__/payment_type.cpython-39.pyc index 2f7c041ef3b0e3183acdc2a8721c82979d00e848..ebfaa8e01d669aecaa1f969f251dc35916af7e24 100644 GIT binary patch literal 689 zcmZ8fO>fjN5Vf7`Zo1nQRV(gr+zSVe2%$nNAvly(0SQHsj5jl7jhv6#4$_|CXY|08 zzvL??egd2rr&%>Hk!Sp7tT)f&sO56b$R0la{&6iB`x(L5QUa&6+yipJfE!lvoOAY) zfq>*2g9Q24Bo~mrWvj_APRCX}%wH&nZ#JK?^d)8fu>(SmFwhd3GHbg0i_Bq>=R4{m1p6z0@?5f*E;tFZR(b}i=1_71!{Ux|=Fq_I#bYkIL-7~K&%-zX delta 224 zcmdnUdW4xTk(ZZ?0SId3TM}k5Oyo<8V*&CUfVfx+NTf1EF{Us?F{Lo3GN&-5FwbGi zWQ<}-VF_l?WWB{5kXV_UnpYB1S&({*DKq63Z*pmINq(+dW@=7~r;8@jEtV3H6h}^R zd>Tm1Pm^`>3Pvp%q4@lytkmR^_{5TuqRgbylGNgr3`M*^`-*rc|71*e1-XHPiR*t6 pGmzn@$$X2UhzTSM;zHHIS$sg&Ee@O9{FKt1R6CHeVi3W@1OSyMH&6fo diff --git a/lightspeed_api/models/__pycache__/price_level.cpython-39.pyc b/lightspeed_api/models/__pycache__/price_level.cpython-39.pyc index 555329280ea8cc264159717759f6c16ff4bff679..d847f0ace21afcd8bcf64a5391e47b3dc0a4150c 100644 GIT binary patch delta 387 zcmYLDJx;?g6n1PkHEC1SE*t@cGav+o1ql@-HZQ@6Uxbm9RE|@Li8EBJT^M@}ZoujT za0mv3Q$@j+-}mG9J-tuzO=lvGBjk^ZKc0Sr?W}lXUS0avEPVmQ1Bo~WA_Iwc7^ox% z97)>a=zv6q{r|W_$#x+R<>%x$jtq0Gk-q`iu;+T46|UkKZbZ~l@Nyv?)o~} WCkD3g%(3&bmaT@1uC3|H6#oH{>1d4r delta 203 zcmaFHa)_BPk(ZZ?0SH*-TM}k6Oyo<8WdZUWfVfx+NTf1EF{Us?F{Lo3GN&-5FwbGi zWQ<}-VF_l?WWB`|P?VXR>XTZQnsbXOGvyX||z9yc=6Vw#2SX zCm!C~pOFXPEyF+IC+sUv{RgGIaL&rI)yAgGAdSxI?B(3{+p{MO0^fk=w?BUW{@cJX z{zRty*uZSSCu1PYU}kI#3}ToDvsfiI2NtnRxxEQUTs&Rp6_dw}JQA z67Wl!cYya<4fvYoUEu3106x%sl{NN_&hlf>Xmm{Fu`uIVG~kcTyu-f}O8(8!3GZfI zoKt>0U^d{BV<6lhCNqe|E6ilpnZ<2Z(X9jaBJVN>TeMcUV5M&TGnZ6Zjd-k1mRLZ1 zUSkcm{K$A{k~(X$)+2)itj$)SZLmvh720L?61xm-lf4YqUs2v3??p1>Liu9yx4m_4 zDLXq&x$vvS9X%}q4JO(fUC;;a*)Q!Dd(zzeFe9@rR@EFk}6jM-QMcB z%8Ho4JtWmgQZ1#1lrxGkQ|$?JbV_+4(#FN(D}{k_b9Z~a$1_^kIc^q1H?Pa}6PXOh zLD8exF78C};C}_ayYR^-ke`hcMg0}Ljxo@1CI~`k9!ZcEj3}Y_URn!)Cv*;&I zv4@5;<)Q^5&P`=Y9v`0@;wrFD5AJ?;_?_fJ9_~fmekj=CR?-~}_%M@)yCOj-$iq15 z^)s1ra2<3#&khpCV|iHkxSpP>TFHbJwB7<@SmR4mXG;?<_>6vQpYe<^W8V;~@T2V& zSEhexzIYXga=LLM`DOUc^_b(vRF59|zZyghWWQ39Kif0}!D8_)ties2xX>{*pyoQ^ zQ9wy5D1EJ@r#1DX7`>niBdddjP|Z-A`4FI?L)L*q(GO++U$x={O4k->%|~}SEd>VR zfudCTl)|2m09{noDgAL2#*>jsG|W^*Wg7H!TAr{%Gz_^HONN_jA>qy=}qJc0Y2npc;!Qjg^AfDx! z)Ay$)bAWmd#y95l%Jn&&WO{B(>*aWD&Q!U+8=hkNIDll_u?1cm${&p4EYew7 zEMZ3=cpT!+%YKx~j;B+Ts^>1|`Bk*ho%e9ICnjlLU@jqT?H!|~#7nB7_DSw^s^U#t z_!g44kz7ZDKosvFxq)PcmT49d@4^5skf`_>$N+enPxSZ&kf1jkxCqIMKmrhPH5jUJ zD|)QjGuaYA!9R0|!)gG9x_BSNkk!K9QnV*9AbvNDyCVo5XUc^n3TH2ez&fl{wNmg^ zkj{1jF35Z@^7z+^OOnDJ_W`WzIC=|6YbKuNAtgbsWx|S&P)~g#n&Oqa({bm;Tsg;a z5@w47j=th25ze!KH~!X9xDaYQ^&2>S7s$d;zpwzo0*Uz$`Z|O@IIBWAqKhImy_)Yw zS)YmU0cjWeQ1ldM(al36Zh|EmqR*W|;C`%01V9l literal 1554 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 F-{0-5y*U5? diff --git a/lightspeed_api/models/__pycache__/serialized.cpython-39.pyc b/lightspeed_api/models/__pycache__/serialized.cpython-39.pyc index 21175713cf869d7cb4eb05c61926f99b837cebb7..6f5b0395d0c0cf35ef97b8a623bc1e4218989269 100644 GIT binary patch literal 770 zcmZ8fO^OsT6i)qiRdw|!g9p%Mfp`EB8JvcpL0YlVMNtql^`a9={g|W|<5rI`7mzuG z_jp@Za{v)s`BL3O50fJAKkxfqC1SBC7_C3o*B^g{jQ#e`-4&B_Lfbwi0Sp4oI##oQ zK?so!y08udcE%uv$lC?g0Uyb5Iu(WDc?^L_%!UZ;CK^BOY9CtbjN(j_b=eRP%ADLf!{ z5AkRtjS^aYldy8Kt4n+EmsNePwn6>CAZ>hm#rMvSu z2WeZQMyG}zW8WH6MRK*oY$LRMt$K-t-CI6-y0vcTq`{)yTdQT2W2gX+wXlwN11KT^ z+*810LdV!6-?HZYmKvCnej3Ie4RepcwFk>?+pen_W}Np#oO3L=Z@i@{jwR<`w?gkT zk?NhH*T68c&fqa0rpw~OIit?EPTHT$Jf{34{(U%o-n^&jZSzjG7s5cZ99rVjJKMZ9 zgTH6nX!ZHR*-_FIVpR8CA|kbI4p;DOyfg_Rq_piL5?Po9_rAYV#kz`jT$%BYb&4Ix W?W`x1!0g3t+?Reb>ubxPjQ#_{9o{$q delta 279 zcmXX=K}rNM5bPwgGAw2e9z2Mm2NCiI5d~59GMBv#!Z^F#LL|dTk~!#6_XEpb^#MM_ zPvjTAKs&1qT~u{f(fzvfrK(QrDRAu{ukJr!T^*jCutGrLpcjo0a0@6=tpF8?H`Pci z=3pl4{P4Q<=)ttQToP8mjMe38ZnDlZCap-R>IhSUl1)wq1C@;V@7ajm1RLCN1)v&q=(gj3JVr`ChNK~pOUD9N1H?eDcJ+x?oisYLT zD@da`m?(8!a^P9rR%$>rAkgID%k%6g)_EoCv5 zu`smo8i&!gHTXV0dmbN|K*}bsQA!g^Su5x(Yp delta 220 zcmaFGx|^9Vk(ZZ?0SF}ITN0)*Oyo<8Vgd3TfVfx+NTf1EF{Us?F{Lo3GN&-5FwbGi zWQ<}-VF_l?WWB{6oLXF*nV)xyDKq63Z*pmINq(+dW@=7~r;8@jEtZnXg4A0aImPj5 zATd8p*2%9JwPb|i^OLetlS|?gOG=6|lS)fci&ruf@dE8C;+<^Dl;jGspM#0(e-Sf~ n;it)bi=l`KBn#p~)xlYOK-Mh|o80`A(wtN~kg{SB!NUXq7^XDt diff --git a/lightspeed_api/models/__pycache__/shipping.cpython-39.pyc b/lightspeed_api/models/__pycache__/shipping.cpython-39.pyc index ca66ebc3c9dabf4e877060d14c5afa672502a255..3c82a8b8609ffccfced8bfff72b12ed9fc6a0db4 100644 GIT binary patch literal 815 zcmZ8f&5qMB5O$pZYzoVY2Y_QF!~-CNR%|7-mxAbWi6V@f@wP_!!*&mQ2JsYq0fcxV zUpeg)AaP>swqn6n=9_W8nfWF$pUnz_>+6s2ACi!epDwt&A`H&)(NE9-fuJKjsYyV9 zLf8d8t!Y4B5r`oELLe6Ob6AIvEJ>B#Vg{)Kn*le%$X?tA`-9>o*W&f+qi9X*ICocx z!8tzqJsLsk07y+m2m#O?6%m9UW9;YR1mXiG2TUD8200XnCs2Bv0q%7G_jV!N-{o}* zr>O9OO_m!uU5$07JzmZ$Y9n(q37d7AT}#n{&7Jh6;r&#njy4I{^B1ELHoulin+5M( z3O{-mTW!W(C|k4#&y`jQ&SX!Hm6-$^EqNzwqS0Q?ZF(^dhPS2)JyoTSKU1RPMvjAP zuB+Tq%sX?vSU-Y9k-(h9x{#V31lo8&aoQGM9UGjy+^2XqJF2pg>s+7uG&!sp2T(%m zMU~l%`8>vK!Ca?pC(th$`?TeqD}zxe2cxJ{8Lh^#v!OQ1p0UyYTgHu1aDwikoZ%^NkkYoVJ?t~W+c0*B+1{69oFqSMV$_S5M` x#aQ2cU;PkGMzi4GUH|f?>)vBHhaDw`GuQKJ8l`2;U7fj>7oDt delta 271 zcmXX=J5B>J5FL*<(JI1QPzE9>5R&hJ5CsC!Y?Cy&*?h)`MZ4OS?JcOu1)_+K3s7+Y zZsNPXvRElK0j`s??eZOV#p&faD*_S?w897h_drN;3nZ<0lLjf9 zgC6bLi^q(eH{5>8V8I9iUJENz-~(%6vOjYi&!6jl(M7B&s?Fd-*WT5(p@&V6UseO;8luXkPK0%I?AD1SjmJu8> ZJIMb)l80!)Eaq2jM?+(;lg%eM!e5D1JT(9S diff --git a/lightspeed_api/models/__pycache__/shop.cpython-39.pyc b/lightspeed_api/models/__pycache__/shop.cpython-39.pyc index 4e9675c2b4a4f233aa1bcda82a5eb82ec3a01b17..4d2f1f9642d61e4e181d60b2fef90660be83f9f0 100644 GIT binary patch literal 1124 zcmZ8gOK%e~5Z+gk&9iM<-VbpBDVP2Lgiu8)Rh3Gq(sHpP8E3~$jGXLZJEiFrenu}` zkPv@`3)i`F;>wW|vztvR*z)-MJR^JNvn|@~hDYtwx36!$S3K`WPOi6z$PNwrhzxk( zYj5f^-}m-B2q66EK`8wXAqyZn_Ij0{K85vsY`hRwz8bxiiA!kZpVlR|L&JV1BR%GW z#{wCG55Z+1BM6JR0#V5;#axAI$!jH#%ekJ<(4gLg#@L6}Wdv>N9cY(q4c1^C)=IVk zUD$-Jl5N2j+<IHsjO@^^B$wZw=z*;|@ zIgvQ5Crd!5MTV}%GgUI!T%E`jVB1J7oJwbQqB4uk6LH?BlgFu9V4Kcdk^Nn44P+u! z=8mPCXV@{<7-u$g#3c znv;v}3fk=zV)YfWWkJ}O&b3p8enoMnMHTis7$`6`f`wYC;f7MeLd}a@&5p91q$3BF zD1mKeCWU$8xXWeDT~f1k3YwC%Je@69ut_T`nOUt=7hALkPfF1k30kPcZ7vhrObS`Z zZ-9@N!eFx~n(IP=x+!b6hrJkM&hux$IW{<-Qc{!-#}?=B=0caAP|X~M))}mv>Qp$4 zMro?C%hTe!aN(R$qq&pzi)S9^_nw>INByV6*YvvVaG;V2S=1+J#TbPix&A+#ONG1+q|KHy6ullbd_OKVCX95Z?=-=Fy T=m|M6ezvUoc^+nS(+d6o9Jp8k delta 197 zcmaFDv6Y!Gk(ZZ?0SM&eTM{NSOyo-oX94mYfVfx+NTf1EF{Us?F{Lo3GN&-5FwbGi zWQ<}-VF_l?WWB`_oRME}izzeZ7H@KCaY=rzTV`rbil>Vv(=C>g%7WBe9681DX&^B_ zP1ebN%vv%+@%c$vsmUeri6tdPnMtK3sl_W9igwgh5km0Av Ve2bxo2_y^RLe;@pe3N}xBmfehFX;dP diff --git a/lightspeed_api/models/__pycache__/tag.cpython-39.pyc b/lightspeed_api/models/__pycache__/tag.cpython-39.pyc index d28d5a790a2540cf4ad5a9a661c3a071a41ce681..a914f96ff6aa35fd50e91fa279832d66b35a243f 100644 GIT binary patch delta 734 zcmZ`#&2G~`5Z+xo!FB93ByE8RXbTi!A+?pb)M?DDb0A8U zLwf}j`31Nlo`Vx_VD3mf1eBQtf`p1!`_0Zb-~9Pq@F}RqVdyjbzJC7r;;B2UPWkTa z?d?|Qobo%Ks;JiiPpw6p0+E6vr656U-e$1kk**CpDuH7G* zY&g)cwP7~aN@LAxLLes@R=mAV97)uMxt=Uqvl8X4vh;nO3rw>49nF-^GJlE2N?laB;4;nKsFj5_AeuUda2-y=@ZH z82ho=w}bsJi3CzM^DoY`hqgn-W&#aXxYYjN@yb%_S5jUm{{E5qhTvep#cODNPXyfi zQFdL9M|fqyeaFLNRb9sNtCa0_V{=9`&sp1XVHefLUz-*+uKuYO$|{j}$$&+umct=-Y? z%<2_FM;Em>v!>8n(Y`Jb=jsQ#OuDF7b%nI2t1qpfHop#E90NDHJA$-L@{}ZgMli8P zmWHvwE?F=VBb&6;;wyZDBsYO6fHeW^CV95C&6(yB_I3;CQF&_jqhTtO{z**c=_#%- znTv3EmKYH0P%{nju3Y4C*@eJ`3OHsa#^oxJSX)BfEJPhDrfxrPtuyO0G?wu!vrca6 zD1~8!O0AhM_LB}L2O^b&emuzL|F4J>lxmn8`|W-!6MU#(gcXp+R-&k^ndnK=k#Y9W7cHY?-qm78|H0h_-8uC;MW diff --git a/lightspeed_api/models/__pycache__/tax.cpython-39.pyc b/lightspeed_api/models/__pycache__/tax.cpython-39.pyc index 7eaed1f58ec2f695fe62c795b2107765f95c7e33..5726225c6cc29c6d0e577420b40e74b1d4bc86a6 100644 GIT binary patch literal 1343 zcmZ`&%We}f6!my!lF6$Lk$9=%Ay^HIO3e}>RMASP5=v29HdZ7vjosF0CKGH2wcWyJ zEeg7jOP#KBP_c6qSI2N(* zD-aK2hzfg*M$|<^G>@rhO(IzpE1qASBrFkai0+na9Au|k5ZQ65&s@vifSv6N>LPnK zk}k;&KTeR+U3@qDIEm@FMnYR*cfbQ6*Sg z8E&&vuCAoHI>I3c;u|x5cj{}9cg1_Z%NjU;fI&#(I?!8QlmcTA98c?|HJ9+|j@;Eb zj*fH5xodL%ah&D;Ebc-zv@SX<%iM*=Y8M~nWo9)+6&+ssdf}P1S{;t9G#5lagduN! z?QT8qzcW&s{+=3~WLosM%fYyiLu>kPb%~{B`nftjv1TM8u56^{Aw(r}(+4r@qca`D z*7%zan1m+5&DRc+pMHFyc&{JR6IisYcttd!37h`)Dvl`T?5jj7+^p$l)2N!c@R`DSte-5I*YN#EH+qEDImr zoPT>Fo`ekE9muG<rZF^ literal 761 zcmbtSJx{|h5Vf784TXx(k%buv@dFS-h$0Y6i}+ZgNXEnlf|91#j?~WdXE3n#FL`C+ zFR;K}ib_E&h?DYk?^(WkcaCVcTLfZjV{-LO33)-X7(S4F_zZ$1lIEnKF{R{~NG81r zk)EQDUd({|G5{Xb+ymZ_P2kO%`!YNyn@g|IgKSdQIuJ(nukTfAQ&7IeyyZH`io{fs zm`SQUZ1}`78vD|RVu4$}5TmZJ>bBHl$1>^KX=Q9#9Azq(Cx=t!d^;X07v_fFpw^oP zF64Y53dOl=abA?N$}wNz{Gk&0=ZTjMtn2WyHpqpwI=il{GE<^g!Bh=z{q9b3X_Pj} zS(e@jEtA7CtqL`;COOq5&SsK4yS=k!sFdVlnEj;CUgajSVzfORYY$4}@CG4dt@m2_ z$9|%P{d99-jK<13KxhE;^b)4?k!D$h+8O}<8DCyd`G=Pfz5LBX2RfpeFAu2n;Ngj8 dkk#v{ooYPzZ4MXi&YpM=FBlwpg+-01@d4Mdp#cB@ diff --git a/lightspeed_api/models/__pycache__/workorder.cpython-39.pyc b/lightspeed_api/models/__pycache__/workorder.cpython-39.pyc index 1646c6eb2784e17c6ad447feb684cba6a11324e5..c3bf2b7a44b5dc3705b481f46ead4e7d0448e5c2 100644 GIT binary patch literal 3272 zcmb7HTXWk)6qaqtmhZVVX>)0shL+X^3J+XM>7>M544o!4EuGP1MyTFREGkQ8cbztQ zW8f$7m_N{Wp7<&I%2SzP243NX@2q4uX_^Ao=$t*PvuBU?eEY2&)aw-szwiB@zu7!x zS-+BT_-Dgd#VxlWn8k9D)wet=XR$mhM7e(6%jc}mEM_z3M~gW;|3kqmz%H^9?2@r< z*e)x>E*sl{U13$&Rbv-fZPRMipJE29mD4T@6jx!NKh32bm5&3-*B^Y#yQ)h*hkx@h zR&mQeAh_k_nC0bpf#q2KAkS@9$h4hl$7l!n(Q{;(X*bj5?0F^A)vUjk={jlFz|~}p zogAAuu-PQ8DK?o|(`=fZV$+#5!)Dklo5`%x>@{7!pNNNvU|i_xqfxWdev;R=+8c1~M$+F7 zdBnUTEomqAid+TjL#4}PZ>pd_&~}_CxIPMm2x7IT3o3Z5-9U7E;V#F>UXnb-$P#G` z^~y5Xj;7ERo=w(idzDQt!XOIwIny;cZo}W+>?H$T zl#sL>>D*H^x4jue98VxAE?Kw2nClAlPz$}y@v>rt)KKcl@#3Z(@Tre|I*Rx2?k^s( z<<%pm*EnLzR=OIx(jP`DG!-%{Bc>_EXQ3&dLy^fm6q(FJkNY2+4pVncBaK5hj{&&M5 z%AOR$ScyEXN8UIoKTt}94~7b_QuitT_Au@el%H7QItCf>*S)oCTlerT za_dgm?FE8uwUh3!&toOGHbg?iCbyz+r>EqAqY}X&eBMNMJd#`E^sf%~go6p_mTy70 zc{lgstC35u^r>n8Ry<=yAEQ%3-ztUt3F1ULEr9NcJ{^|QY4Km^v;?{*+N`n~s~h@k z8a)BxBzp>!N%|DXlJqRD(`*)$cLoGFchCgg&3k3G0LohwD@e_2j#q*Dn+3)mK9>SqLsGleHnjE(k6d3`Bx0A!_7YC+9{eHwU~64p}D>HTXA6BZ}m+Kw_4} zX%b5$&XHIoaTY?`lz=W0@Q?=1P)p2^n1pCG#YJjgg3vBdmFDCOOg?5ejZ(Tyel7Bw zpgoi3BX@E_0i`UWIU%o%W_i@Dgx02+%^6zP8$DXh@*8~r69jRoQOCl zLtG(4T;Vyy74U(P9_Z--o*|BxfH~?f0ePt&9pFtI-Fb6xlcpn^G#%EY>7XV(hijhA z0f`Gh;v%lIY%#Nz4yJ+2bHL>?aCu%_zzZD0C6Vz6m&B?mIGtWH6jGP^VN9&yO^)HY zz6yTRi%90F2T!0)EjX}6|R z%$l4DxZ+C^w;;TR0j`fdl&0E7r?$ID(<=eBCeu&JdyHoBIhjWRD>~Fic*RFZ@YwHC z3NME)@%c&6HAu}<$S+73^u8RpR4IbKyaMrZ;EqZ2_u`DH@G$@r@vKrXs@sVG9#JN~ zlMN8<6u_)>;CK#my&@|EL=~}&Ac8iPAvAoM;e5jPRw9()C$HR7{b((T65&QeS_UocC8=#uDjCdb?G6Ay51VTA){0$1NKWhgSFR wV#&N7g{E?8OVGcI6w52rqPnCj@B+Hg?)CJDzCnW}QGG2}%TFv$%uLMv1Ci2Uxc~qF literal 929 zcmb`GK}*~~6vt;K$(l`Fv=(~s(pzCKJ@i;gDOD7qtfELE1jd;6TBl7m>&yi4Tt7y? zz<#B<_SCQ7rM?&IT1yXtPRO76|3l{e=4DcjMwy`T`o+c3OC`ilHh+(p%zL`s9tjAL zwP<86rPvmrAi59`q5K}@ihK+S`K0#|`2j4EU+R5KK7}FqVeb>j4#fD$FGjh*2_jb@F=Bup%E&y1m=;UI@KbBQwHD9@Wgv{I&iS_4GFZdT z=3Ir?pU*I)wbP$Do6M7tYCSC*)H-BZH!XBE&zH46?aKPklgLbcSkrBfr%UgxIqp2V zxv(pQ)XU}J#+%~Mp>@T+sZL4@#b#S|4Nko)_H4^Lb46{woOm}w1YOR|zn02l?TWLT zO}v_&+lUHq0~|}Lvfq93fF6w&J=*cuSYFm9ECZ5>9rARstLxb+XHQA^sN8~Z@+cr5 z%@pqsvPNZ@=oW;N2Y{?BI&$Dk-?{svJfp(T@1Srp84rT6^fQV6TlJl@%xL-P|C$@p Sx7SDS9lzyLl4Z3z;OhzvaJ)YN diff --git a/lightspeed_api/models/catalog.py b/lightspeed_api/models/catalog.py index 0db6926..65a42d5 100644 --- a/lightspeed_api/models/catalog.py +++ b/lightspeed_api/models/catalog.py @@ -1,13 +1,58 @@ +from datetime import datetime, date + from . import BaseObject -class CatalogVendorItem(BaseObject): + +class CatalogVendor(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "catalogVendorID"}, + "name": {"type": str, "ls_field": "name"}, + "last_updated": {"type": datetime, "ls_field": "lastUpdate"}, } -class Vendor(BaseObject): +class CatalogVendorAvailability(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "catalogVendorID"}, + } + + +class CatalogVendorItem(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "catalogVendorItemID"}, + "vendor_number": {"type": str, "ls_field": "vendorNumber"}, + "description": {"type": str, "ls_field": "description"}, + "category": {"type": str, "ls_field": "category"}, + "model": {"type": str, "ls_field": "model"}, + "unit_count": {"type": int, "ls_field": "retailUnit"}, + "units": {"type": str, "ls_field": "unitOfMeasurement"}, + "cost": {"type": float, "ls_field": "cost"}, + "cost_discount_1": {"type": float, "ls_field": "costLevel2"}, + "cost_discount_2": {"type": float, "ls_field": "costLevel3"}, + "cost_discount_3": {"type": float, "ls_field": "costLevel4"}, + "msrp": {"type": float, "ls_field": "msrp"}, + "break_count_1": {"type": int, "ls_field": "breakQty"}, + "break_price_1": {"type": float, "ls_field": "breakPrice"}, + "break_count_2": {"type": int, "ls_field": "breakQty2"}, + "break_price_2": {"type": float, "ls_field": "breakPrice2"}, + "break_count_3": {"type": int, "ls_field": "breakQty3"}, + "break_price_3": {"type": float, "ls_field": "breakPrice3"}, + "manufacturer_number": {"type": str, "ls_field": "manufacturerNumber"}, + "brand": {"type": str, "ls_field": "brand"}, + "last_price_change": {"type": date, "ls_field": "lastPriceChange"}, + "upc": {"type": str, "ls_field": "upc"}, + "ean": {"type": str, "ls_field": "ean"}, + "alternate_ean": {"type": str, "ls_field": "ean2"}, + "status": {"type": str, "ls_field": "status"}, + "year": {"type": str, "ls_field": "year"}, + "replacement_item": {"type": str, "ls_field": "replacement"}, + "replacement_description": {"type": str, "ls_field": "replacementDescription"}, + "color": {"type": str, "ls_field": "colorName"}, + "size": {"type": str, "ls_field": "sizeName"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "last_vendor_quantity": {"type": int, "ls_field": "lastQoh"}, + "vendor": {"type": CatalogVendor, "ls_field": "CatalogVendor", "ls_field_id": "catalogVendorID", "relationships": ["CatalogVendor"]}, + "vendor_availability": {"type": CatalogVendorAvailability, "multifield": True, "ls_field": "CatalogVendorAvailability", "relationships": ["CatalogVendor"]}, + } + _create_url = 'CatalogVendorItem.json' \ No newline at end of file diff --git a/lightspeed_api/models/category.py b/lightspeed_api/models/category.py index 625285d..869a7e8 100644 --- a/lightspeed_api/models/category.py +++ b/lightspeed_api/models/category.py @@ -1,7 +1,18 @@ +from datetime import datetime + from . import BaseObject + class Category(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "categoryID"}, + "name": {"type": int, "ls_field": "name"}, + "created_time": {"type": int, "ls_field": "createTime"}, + "last_modified_time": {"type": int, "ls_field": "timeStamp"}, + "parent_category": {"type": 'Category', "ls_field": "Parent", "ls_field_id": "parentID", "relationships": ["Parent"]}, + "node_depth": {"type": str, "ls_field": "nodeDepth"}, + "full_path": {"type": str, "ls_field": "fullPathName"}, + "left_node": {"type": int, "ls_field": "leftNode"}, + "right_node": {"type": int, "ls_field": "rightNode"}, } \ No newline at end of file diff --git a/lightspeed_api/models/contact.py b/lightspeed_api/models/contact.py new file mode 100644 index 0000000..38f870a --- /dev/null +++ b/lightspeed_api/models/contact.py @@ -0,0 +1,16 @@ + +from datetime import datetime +from . import BaseObject + + +class Contact(BaseObject): + _object_attributes = { + "is_email_allowed": {"type": bool, "ls_field": "noEmail"}, + "is_phone_allowed": {"type": bool, "ls_field": "noPhone"}, + "is_mail_allowed": {"type": bool, "ls_field": "noMail"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "emails": {"type": 'CustomerEmail', "multifield": True, "ls_field": "Emails.ContactEmail"}, + "phone_numbers": {"type": 'CustomerPhoneNumber', "multifield": True, "ls_field": "Phones.ContactPhone"}, + "addresses": {"type": 'CustomerAddress', "multifield": True, "ls_field": "Addresses.ContactAddress"}, + "website": {"type": str, "ls_field": "Websites.ContactWebsite", "ls_secondary_field": "url", "optional": True}, + } diff --git a/lightspeed_api/models/credit_account.py b/lightspeed_api/models/credit_account.py index ddabde6..b43ede0 100644 --- a/lightspeed_api/models/credit_account.py +++ b/lightspeed_api/models/credit_account.py @@ -1,7 +1,23 @@ +from datetime import datetime + from . import BaseObject + class CreditAccount(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "creditAccountID"}, + "name": {"type": str, "ls_field": "name"}, + "code": {"type": str, "ls_field": "code"}, + "description": {"type": str, "ls_field": "description"}, + "is_giftcard": {"type": bool, "ls_field": "giftCard"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "customer": {"type": 'Customer', "ls_field": "Contact", "ls_field_id": "customerID", "relationships": ["Contact"]}, # TODO: might actually be a "Contact" object from a customer + "withdrawals": {"type": 'SalePayment', "multifield": True, "ls_field": "WithdrawalPayments.Payment", "relationships": ["WithdrawalPayments"]}, + "last_modified_time": {"type": int, "ls_field": "timeStamp"}, + "balance": {"type": float, "ls_field": "balance"}, + } + _get_function = "CreditAccountsAPI.get_customer" + _update_url = 'CreditAccount/%s.json' + _create_url = 'CreditAccount.json' + _delete_url = 'CreditAccount/%s.json' \ No newline at end of file diff --git a/lightspeed_api/models/credit_card.py b/lightspeed_api/models/credit_card.py index f578495..1b6ebbb 100644 --- a/lightspeed_api/models/credit_card.py +++ b/lightspeed_api/models/credit_card.py @@ -1,7 +1,49 @@ +from datetime import datetime from . import BaseObject + class CreditCardCharge(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "ccChargeID"}, + "transaction_id": {"type": str, "ls_field": "gatewayTransID"}, + "card_last_4_digits": {"type": str, "ls_field": "xnum"}, + "response": {"type": str, "ls_field": "response"}, + "is_voided": {"type": bool, "ls_field": "voided"}, + "refunded_amount": {"type": float, "ls_field": "refunded"}, + "charged_amount": {"type": float, "ls_field": "amount"}, + "card_expiry": {"type": str, "ls_field": "exp"}, + "is_auth_only": {"type": bool, "ls_field": "authOnly"}, + "authorization_code": {"type": str, "ls_field": "authCode"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "is_declined": {"type": bool, "ls_field": "declined"}, + "entry_method": {"type": str, "ls_field": "entryMethod"}, + "cardholder_name": {"type": str, "ls_field": "cardholderName"}, + "communication_key": {"type": str, "ls_field": "communicationKey"}, + "sale": {"type": 'Sale', "ls_field_id": "saleID"}, + "payments": {"type": 'SalePayment', "multifield": True, "ls_field": "SalePayments", "relationships": ["SalePayments"]}, + } + + +class CreditCardGateway(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "ccGatewayID"}, + "login": {"type": str, "ls_field": "login"}, + "market_type": {"type": str, "ls_field": "marketType"}, + "transaction_key": {"type": str, "ls_field": "transKey"}, + "device_type": {"type": int, "ls_field": "deviceType"}, + "hash": {"type": str, "ls_field": "hashValue"}, + "is_enabled": {"type": bool, "ls_field": "enabled"}, + "is_test_mode_enabled": {"type": bool, "ls_field": "testMode"}, + "gateway_name": {"type": str, "ls_field": "gateway"}, + "account_number": {"type": str, "ls_field": "accountNum"}, + "terminal_id": {"type": str, "ls_field": "terminalNum"}, + "is_credit_card_charges_allowed": {"type": bool, "ls_field": "allowCredits"}, + "other_credentials_1": {"type": str, "ls_field": "otherCredentials1"}, + "other_credentials_2": {"type": str, "ls_field": "otherCredentials2"}, + "visa_payment_type": {"type": 'PaymentType', "ls_field_id": "visaPaymentTypeID"}, + "mastercard_payment_type": {"type": 'PaymentType', "ls_field_id": "masterPaymentTypeID"}, + "discover_payment_type": {"type": 'PaymentType', "ls_field_id": "discoverPaymentTypeID"}, + "debit_payment_type": {"type": 'PaymentType', "ls_field_id": "debitPaymentTypeID"}, + "american_payment_type": {"type": 'PaymentType', "ls_field_id": "americanPaymentTypeID"}, } \ No newline at end of file diff --git a/lightspeed_api/models/customer.py b/lightspeed_api/models/customer.py index f501127..db5e3c8 100644 --- a/lightspeed_api/models/customer.py +++ b/lightspeed_api/models/customer.py @@ -3,11 +3,6 @@ from enum import Enum from . import BaseObject -from .credit_account import CreditAccount -from .discount import Discount -from .note import Note -from .tag import Tag -from .tax import TaxCategory from ..api import ClientAPI @@ -16,8 +11,8 @@ class CustomerType(BaseObject): _object_attributes = { "id": {"type": int, "ls_field": "customerTypeID"}, "name": {"type": str, "ls_field": "name"}, - "tax_category": {"type": TaxCategory, "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, # TODO: there are TaxCategory.TaxCategoryClasses and TaxCategory.TaxCategoryClasses.TaxClass relationships for this item too - should we load them all? - "discount": {"type": Discount, "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]} + "tax_category": {"type": 'TaxCategory', "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, # TODO: there are TaxCategory.TaxCategoryClasses and TaxCategory.TaxCategoryClasses.TaxClass relationships for this item too - should we load them all? + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]} } _get_function = "CustomersAPI.get_customer_type" @@ -38,7 +33,8 @@ class CustomerCustomField(BaseObject): class CustomerCustomFieldValue(BaseObject): _object_attributes = { "id": {"type": int, "ls_field": "customFieldValueID"}, - "custom_field": {"type": CustomerCustomField, "ls_field_id": "customFieldID"}, + "custom_field": {"type": 'CustomerCustomField', "ls_field_id": "customFieldID"}, + #"is_deleted": {"type": bool, "ls_field": "deleted"}, "name": {"type": str, "ls_field": "name"}, "type": {"type": str, "ls_field": "type"}, # TODO: do we want to convert this to a Python type? "value": {"type": str, "ls_field": "value"}, # TODO: do we want to convert this to its Python type? @@ -47,7 +43,11 @@ class CustomerCustomFieldValue(BaseObject): class CustomerCustomFieldChoice(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "customFieldChoiceID"}, + "name": {"type": str, "ls_field": "name"}, + "value": {"type": str, "ls_field": "value"}, + "is_deletable": {"type": bool, "ls_field": "canBeDeleted"}, + "custom_field": {"type": 'CustomerCustomField', "ls_field_id": "customFieldID"}, } @@ -125,17 +125,14 @@ class Customer(BaseObject): "vat_number": {"type": str, "ls_field": "vatNumber"}, "created_time": {"type": datetime, "ls_field": "createTime"}, "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, - "emails": {"type": CustomerEmail, "multifield": True, "ls_field": "Contact.Emails.ContactEmail", "relationships": ["Contact"]}, - "phone_numbers": {"type": CustomerPhoneNumber, "multifield": True, "ls_field": "Contact.Phones.ContactPhone", "relationships": ["Contact"]}, - "addresses": {"type": CustomerAddress, "multifield": True, "ls_field": "Contact.Addresses.ContactAddress", "relationships": ["Contact"]}, - "website": {"type": str, "ls_field": "Contact.Websites.ContactWebsite", "ls_secondary_field": "url", "optional": True, "relationships": ["Contact"]}, - "tags": {"type": Tag, "multifield": True, "ls_field": "Tags.Tag", "relationships": ["Tags"]}, - "credit_account": {"type": CreditAccount, "ls_field": "CreditAccount", "ls_field_id": "creditAccountID", "relationships": ["CreditAccount"]}, - "type": {"type": CustomerType, "ls_field": "CustomerType", "ls_field_id": "customerTypeID", "relationships": ["CustomerType"]}, - "discount": {"type": Discount, "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, - "tax_category": {"type": TaxCategory, "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, - "note": {"type": Note, "ls_field": "Note", "relationships": ["Note"], 'optional': True}, - "custom_fields": {"type": CustomerCustomFieldValue, "multifield": True, "ls_field": "CustomFieldValues.CustomFieldValue", "relationships": ["CustomFieldValues", "CustomFieldValues.value"]}, + "contact": {"type": 'Contact', 'optional': True, "ls_field": "Contact", "relationships": ["Contact"]}, + "tags": {"type": 'Tag', "multifield": True, "ls_field": "Tags.Tag", "relationships": ["Tags"]}, + "credit_account": {"type": 'CreditAccount', "ls_field": "CreditAccount", "ls_field_id": "creditAccountID", "relationships": ["CreditAccount"]}, + "type": {"type": 'CustomerType', "ls_field": "CustomerType", "ls_field_id": "customerTypeID", "relationships": ["CustomerType"]}, + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, + "tax_category": {"type": 'TaxCategory', "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, + "note": {"type": 'Note', "ls_field": "Note", "relationships": ["Note"], 'optional': True}, + "custom_fields": {"type": 'CustomerCustomFieldValue', "multifield": True, "ls_field": "CustomFieldValues.CustomFieldValue", "relationships": ["CustomFieldValues", "CustomFieldValues.value"]}, } _get_function = "CustomersAPI.get_customer" _update_url = 'Customer/%s.json' diff --git a/lightspeed_api/models/discount.py b/lightspeed_api/models/discount.py index 96afe27..1a0cfa6 100644 --- a/lightspeed_api/models/discount.py +++ b/lightspeed_api/models/discount.py @@ -1,7 +1,19 @@ +from datetime import datetime + from . import BaseObject + class Discount(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "discountID"}, + "name": {"type": str, "ls_field": "name"}, + "amount": {"type": float, "ls_field": "discountAmount"}, + "percentage": {"type": float, "ls_field": "discountPercent"}, + "is_customer_required": {"type": bool, "ls_field": "requireCustomer"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + } + _update_url = 'Discount/%s.json' + _create_url = 'Discount.json' + _delete_url = 'Discount/%s.json' \ No newline at end of file diff --git a/lightspeed_api/models/employee.py b/lightspeed_api/models/employee.py index 508fcc2..30c23ce 100644 --- a/lightspeed_api/models/employee.py +++ b/lightspeed_api/models/employee.py @@ -1,13 +1,54 @@ +from datetime import datetime + from . import BaseObject + +class EmployeeRight(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "employeeRightID"}, + "name": {"type": str, "ls_field": "name"}, + } + + +class EmployeeRole(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "employeeRoleID"}, + "name": {"type": str, "ls_field": "name"}, + "emails": {"type": 'EmployeeRight', "multifield": True, "ls_field": "EmployeeRights"}, + } + + class Employee(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "employeeID"}, + "first_name": {"type": str, "ls_field": "firstName"}, + "last_name": {"type": str, "ls_field": "lastName"}, + "is_locked_out": {"type": bool, "ls_field": "lockOut"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "customer": {"type": 'Customer', "ls_field": "Contact", "ls_field_id": "contactID", "relationships": ["Contact"]}, # TODO: might actually be a "Contact" object from a customer + "currently_clocked_in_hours": {"type": 'EmployeeHours', "ls_field": "clockInEmployeeHoursID"}, + "role": {"type": 'EmployeeRole', "ls_field": "Contact", "ls_field_id": "contactID", "relationships": ["EmployeeRole", "EmployeeRole.EmployeeRights"]}, + "rights": {"type": 'EmployeeRight', "multifield": True, "ls_field": "EmployeeRights", "relationships": ["EmployeeRights"]}, + "limited_to_shop": {"type": 'Shop', "ls_field_id": "limitToShopID"}, + "last_shop": {"type": 'Shop', "ls_field_id": "lastShopID"}, + "last_sale": {"type": 'Sale', "ls_field_id": "lastSaleID"}, + "last_register": {"type": 'Register', "ls_field_id": "lastRegisterID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, } + _update_url = 'Employee/%s.json' + _create_url = 'Employee.json' + _delete_url = 'Employee/%s.json' class EmployeeHours(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "employeeHoursID"}, + "start": {"type": datetime, "ls_field": "checkIn"}, + "end": {"type": datetime, "ls_field": "checkOut"}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "shop": {"type": 'Shop', "ls_field_id": "shopID"}, + } + _update_url = 'EmployeeHours/%s.json' + _create_url = 'EmployeeHours.json' + _delete_url = 'EmployeeHours/%s.json' \ No newline at end of file diff --git a/lightspeed_api/models/image.py b/lightspeed_api/models/image.py index dc7935f..3529b1e 100644 --- a/lightspeed_api/models/image.py +++ b/lightspeed_api/models/image.py @@ -3,5 +3,14 @@ class Image(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "imageID"}, + "description": {"type": str, "ls_field": "description"}, + "filename": {"type": str, "ls_field": "filename"}, + "order": {"type": int, "ls_field": "ordering"}, + "public_image_id": {"type": str, "ls_field": "publicID"}, + "item": {"type": 'Item', "ls_field": "Item", "ls_field_id": "itemID", "relationships": ["Item"]}, + "item_matrix": {"type": 'ItemMatrix', "ls_field": "ItemMatrix", "ls_field_id": "itemMatrixID", "relationships": ["ItemMatrix"]}, + } + + # TODO: build API that can upload images + # TODO: build function that can download images (with settings for height/width/scale/etc.) \ No newline at end of file diff --git a/lightspeed_api/models/industry.py b/lightspeed_api/models/industry.py index f6aea16..507e07f 100644 --- a/lightspeed_api/models/industry.py +++ b/lightspeed_api/models/industry.py @@ -3,5 +3,7 @@ class Industry(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "industryID"}, + "name": {"type": str, "ls_field": "name"}, + "is_enabled": {"type": bool, "ls_field": "enabled"}, } \ No newline at end of file diff --git a/lightspeed_api/models/inventory.py b/lightspeed_api/models/inventory.py index c8ef193..7ab28dd 100644 --- a/lightspeed_api/models/inventory.py +++ b/lightspeed_api/models/inventory.py @@ -1,13 +1,78 @@ +from datetime import datetime + from . import BaseObject class InventoryCount(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "inventoryCountID"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "name": {"type": str, "ls_field": "name"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "shop": {"type": 'Shop', "ls_field_id": "shopID"}, + } + + +class InventoryCountItem(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "inventoryCountItemID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "quantity": {"type": int, "ls_field": "qty"}, + "inventory": {"type": 'InventoryCount', "ls_field_id": "inventoryCountID"}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "item": {"type": 'Item', "ls_field_id": "itemID"}, + } + + +class InventoryCountCalculation(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "inventoryCountCalcID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "expected_count": {"type": int, "ls_field": "calcQoh"}, + "actual_count": {"type": int, "ls_field": "countedQoh"}, + "inventory": {"type": 'InventoryCount', "ls_field_id": "inventoryCountID"}, + "item": {"type": 'Item', "ls_field_id": "shopID"}, + } + + +class InventoryCountReconciliation(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "inventoryCountReconcileID"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "cost_change": {"type": float, "ls_field": "costChange"}, + "count_change": {"type": int, "ls_field": "qohChange"}, + "inventory": {"type": 'InventoryCount', "ls_field_id": "inventoryCountID"}, + "item": {"type": 'Item', "ls_field_id": "shopID"}, } class InventoryTransfer(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "transferID"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "note": {"type": str, "ls_field": "note"}, + "status": {"type": str, "ls_field": "status"}, + "sent_time": {"type": datetime, "ls_field": "sentOn"}, + "needed_by_time": {"type": datetime, "ls_field": "needBy"}, + "created_employee": {"type": 'Employee', "ls_field_id": "sentByEmployeeID", "relationships": ["CreatedByEmployee", "CreatedByEmployee.Contact"]}, + "sending_employee": {"type": 'Employee', "ls_field_id": "sentByEmployeeID", "relationships": ["SentByEmployee", "SentByEmployee.Contact"]}, + "sending_shop": {"type": 'Shop', "ls_field_id": "sendingShopID", "relationships": ["SendingShop", "SendingShop.Contact"]}, + "receiving_shop": {"type": 'Shop', "ls_field_id": "receivingShopID", "relationships": ["ReceivingShop", "ReceivingShop.Contact"]}, + } + +class InventoryTransferItem(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "transferItemID"}, + "count_sending": {"type": int, "ls_field": "toSend"}, + "count_receiving": {"type": int, "ls_field": "toReceive"}, + "currently_sent": {"type": int, "ls_field": "sent"}, + "currently_received": {"type": int, "ls_field": "received"}, + "value_sent": {"type": float, "ls_field": "sentValue"}, + "value_received": {"type": float, "ls_field": "receivedValue"}, + "comment": {"type": str, "ls_field": "comment"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "transfer": {"type": 'InventoryTransfer', "ls_field_id": "transferID"}, + "item": {"type": 'Item', "ls_field_id": "itemID"}, } \ No newline at end of file diff --git a/lightspeed_api/models/item.py b/lightspeed_api/models/item.py index 691ecf7..e84fc73 100644 --- a/lightspeed_api/models/item.py +++ b/lightspeed_api/models/item.py @@ -14,49 +14,25 @@ class Item(BaseObject): "manufacturer_sku": {"type": str, "ls_field": "manufacturerSku"}, "default_cost": {"type": float, "ls_field": "defaultCost"}, "average_cost": {"type": float, "ls_field": "avgCost"}, - "taxed": {"type": bool, "ls_field": "tax"}, - "archived": {"type": bool, "ls_field": "archived"}, - "discountable": {"type": bool, "ls_field": "discountable"}, - "serialized": {"type": bool, "ls_field": "serialized"}, - "added_to_ecom": {"type": bool, "ls_field": "publishToEcom"}, + "is_taxed": {"type": bool, "ls_field": "tax"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "is_discountable": {"type": bool, "ls_field": "discountable"}, + "is_serialized": {"type": bool, "ls_field": "serialized"}, + "is_publishable_to_ecom": {"type": bool, "ls_field": "publishToEcom"}, "type": {"type": str, "ls_field": "systemSku"}, # TODO: handle enum values: default, non_inventory, serialized, box, serialized_assembly, assembly "description": {"type": str, "ls_field": "description"}, "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, "created_time": {"type": datetime, "ls_field": "createTime"}, "tags": {"type": Tag, "multifield": True, "ls_field": "Tags.tag", "relationships": ["TagRelations.Tag"]}, - # TODO: make these map to objects dynamically instead of just an ID - "category_id": {"type": int, "ls_field": "categoryID"}, - "item_matrix_id": {"type": int, "ls_field": "itemMatrixID"}, - "manufacturer_id": {"type": int, "ls_field": "manufacturerID"}, - "tax_class_id": {"type": int, "ls_field": "taxClassID"}, - "default_vendor_id": {"type": int, "ls_field": "defaultVendorID"}, - # TODO: Note field - # TODO: Category, TaxClass, ItemAttributes, Manufacturer, ItemShops, - # ItemComponents, ItemShelfLocations, ItemVendorNums fields - # TODO: CustomFieldValues field + "category": {"type": 'Category', "ls_field": "categoryID", "relationships": ["Category"]}, + "item_matrix": {"type": 'ItemMatrix', "ls_field": "itemMatrixID"}, + "manufacturer": {"type": 'Manufacturer', "ls_field": "manufacturerID", "relationships": ["Manufacturer"]}, + "tax_class": {"type": 'TaxClass', "ls_field": "taxClassID", "relationships": ["TaxClass"]}, + "default_vendor": {"type": "Vendor", "ls_field": "defaultVendorID"}, + # TODO: Images, ItemAttributes, ItemShops, ItemComponents, ItemShelfLocations, ItemVendorNums fields + "note": {"type": 'Note', "ls_field": "Note", "relationships": ["Note"], 'optional': True}, + "custom_fields": {"type": 'ItemCustomFieldValue', "multifield": True, "ls_field": "CustomFieldValues.CustomFieldValue", "relationships": ["CustomFieldValues", "CustomFieldValues.value"]}, } - def __init__(self, obj=None, api=None): - if obj: - # TODO: figure out if fields (like self.name) are just references to fields in the JSON blob or extracted as-is - self.id = obj['itemID'] - self.created_at = obj['createTime'] - self.name = obj['description'] - self.tags = [] - self.json_object = obj - - # TODO: move this + setter into BaseObject - # TODO: setter function should have format function to set up the object (i.e. converting from str to obj)? - @property - def tags(self): - if not self._tags: - self._tags = self.api.items.get_tags_for_item(self.id) - return self._tags # TODO: return list object that allows you to add/delete from the list via just a string (instead of a tag object)? - - @tags.setter - def tags(self, tags): - self._tags = [] - for t in tags: - self._tags.append(Tag({"name": t, "tagID": None})) def get_associated_sales(self, created_at=None): return self.api.sales.get_sales_for_item(self.id, created_at=created_at) @@ -64,26 +40,67 @@ def get_associated_sales(self, created_at=None): def __repr__(self): return self.name + # TODO: get_image function class ItemCustomField(BaseObject): _object_attributes = { "id": {"type": int, "ls_field": "customFieldID"}, + "name": {"type": str, "ls_field": "name"}, + "type": {"type": str, "ls_field": "type"}, + "units": {"type": str, "ls_field": "uom"}, + "decimal_precision": {"type": int, "ls_field": "decimalPrecision"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "default": {"type": str, "ls_field": "default"}, # TODO: may be a mixed type, so this may not work? } -class ItemCustomFieldChoice(BaseObject): +class ItemCustomFieldValue(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "customFieldValueID"}, + "custom_field": {"type": 'ItemCustomField', "ls_field_id": "customFieldID"}, + "is_deleted": {"type": bool, "ls_field": "deleted"}, + "name": {"type": str, "ls_field": "name"}, + "type": {"type": str, "ls_field": "type"}, # TODO: do we want to convert this to a Python type? + "value": {"type": str, "ls_field": "value"}, # TODO: do we want to convert this to its Python type? + } + + +class ItemCustomFieldChoice(BaseObject): + _object_attributes = { # TODO: same as customer? + "id": {"type": int, "ls_field": "customFieldChoiceID"}, + "name": {"type": str, "ls_field": "name"}, + "value": {"type": str, "ls_field": "value"}, + "is_deletable": {"type": bool, "ls_field": "canBeDeleted"}, + "custom_field": {"type": 'ItemCustomField', "ls_field_id": "customFieldID"}, } class ItemAttributeSet(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "itemAttributeSetID"}, + "name": {"type": str, "ls_field": "name"}, + "attribute_1": {"type": str, "ls_field": "attributeName1"}, + "attribute_2": {"type": str, "ls_field": "attributeName2"}, + "attribute_3": {"type": str, "ls_field": "attributeName3"}, } class ItemMatrix(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "itemMatrixID"}, + "default_cost": {"type": float, "ls_field": "defaultCost"}, + "is_taxed": {"type": bool, "ls_field": "tax"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "is_serialized": {"type": bool, "ls_field": "serialized"}, + "type": {"type": str, "ls_field": "systemSku"}, # TODO: handle enum values: default, non_inventory, serialized, box, serialized_assembly, assembly + "description": {"type": str, "ls_field": "description"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "category": {"type": 'Category', "ls_field": "categoryID", "relationships": ["Category"]}, + "manufacturer": {"type": 'Manufacturer', "ls_field": "manufacturerID", "relationships": ["Manufacturer"]}, + "tax_class": {"type": 'TaxClass', "ls_field": "taxClassID", "relationships": ["TaxClass"]}, + "default_vendor": {"type": "Vendor", "ls_field": "defaultVendorID"}, + "item_attributes": {"type": 'ItemAttributeSet', "ls_field": "itemAttributeSetID", "relationships": ["ItemAttributeSet"]}, + "items": {"type": 'Item', "multifield": True, "ls_field": "Items.Item", "relationships": ["Items"]}, + "custom_fields": {"type": 'ItemCustomFieldValue', "multifield": True, "ls_field": "CustomFieldValues.CustomFieldValue", "relationships": ["CustomFieldValues", "CustomFieldValues.value"]}, + } + # TODO: images attribute \ No newline at end of file diff --git a/lightspeed_api/models/locale.py b/lightspeed_api/models/locale.py index 418e80a..e28d81c 100644 --- a/lightspeed_api/models/locale.py +++ b/lightspeed_api/models/locale.py @@ -1,7 +1,28 @@ from . import BaseObject +class CurrencyDenomination(BaseObject): + _object_attributes = { + "name": {"type": str, "ls_field": "name"}, + "value": {"type": str, "ls_field": "value"}, + "is_significant": {"type": bool, "ls_field": "significant"}, + } + + class Locale(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "name": {"type": str, "ls_field": "name"}, + "country": {"type": str, "ls_field": "country"}, + "states": {"type": str, "ls_field": "states"}, # TODO: formatting of this magically? from list to concat'd string? + "currency_symbol": {"type": str, "ls_field": "currencySymbol"}, + "currency_code": {"type": str, "ls_field": "currencyCode"}, + "currency_precision": {"type": float, "ls_field": "currencyPrecision"}, + "cash_rounding_precision": {"type": float, "ls_field": "cashRoundingPrecision"}, + "is_include_tax_on_labels": {"type": bool, "ls_field": "includeTaxOnLabels"}, + "language_tag": {"type": str, "ls_field": "languageTag"}, + "date_format": {"type": str, "ls_field": "dateFormat"}, + "datetime_format": {"type": str, "ls_field": "dateTimeFormat"}, + "denominations": {"type": 'CurrencyDenomination', "multifield": True, "ls_field": "CurrencyDenominations.CurrencyDenomination"}, + "tax_name_1": {"type": str, "ls_field": "taxName1"}, + "tax_name_2": {"type": str, "ls_field": "taxName2"}, } \ No newline at end of file diff --git a/lightspeed_api/models/manufacturer.py b/lightspeed_api/models/manufacturer.py index d0d0d9d..996087f 100644 --- a/lightspeed_api/models/manufacturer.py +++ b/lightspeed_api/models/manufacturer.py @@ -1,7 +1,11 @@ +from datetime import datetime from . import BaseObject class Manufacturer(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "manufacturerID"}, + "name": {"type": str, "ls_field": "name"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, } \ No newline at end of file diff --git a/lightspeed_api/models/options.py b/lightspeed_api/models/options.py index 6557a17..9c5895c 100644 --- a/lightspeed_api/models/options.py +++ b/lightspeed_api/models/options.py @@ -1,7 +1,15 @@ from . import BaseObject + +class Option(BaseObject): + _object_attributes = { + "name": {"type": str, "ls_field": "name"}, + "value": {"type": str, "ls_field": "value"}, + } + + class AccountOption(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": 'Option', "multifield": True, "ls_field": "Option"}, } \ No newline at end of file diff --git a/lightspeed_api/models/order.py b/lightspeed_api/models/order.py index 3a3f469..8aac0ec 100644 --- a/lightspeed_api/models/order.py +++ b/lightspeed_api/models/order.py @@ -1,25 +1,75 @@ +from datetime import datetime from . import BaseObject + class Order(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "orderID"}, + "ordered_time": {"type": datetime, "ls_field": "orderedDate"}, + "received_time": {"type": datetime, "ls_field": "receivedDate"}, + "arrival_time": {"type": datetime, "ls_field": "arrivalDate"}, + "reference_number": {"type": str, "ls_field": "refNum"}, + "shipping_instructions": {"type": str, "ls_field": "shipInstructions"}, + "stock_instructions": {"type": str, "ls_field": "stockInstructions"}, + "shipping_costs": {"type": float, "ls_field": "shipCost"}, + "other_costs": {"type": float, "ls_field": "otherCost"}, + "is_complete": {"type": bool, "ls_field": "complete"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "discount_percent": {"type": float, "ls_field": "discount"}, + "total_discount": {"type": float, "ls_field": "totalDiscount"}, + "total_quantity": {"type": int, "ls_field": "totalQuantity"}, + "vendor": {"type": 'Vendor', "ls_field": "Vendor", "ls_field_id": "vendorID", "relationships": ["Vendor"]}, + "note": {"type": 'Note', "ls_field": "Note", "ls_field_id": "noteID", "relationships": ["Note"]}, + "shop": {"type": 'Shop', "ls_field": "Shop", "ls_field_id": "shopID", "relationships": ["Shop"]}, + "custom_fields": {"type": 'OrderCustomFieldValue', "multifield": True, "ls_field": "CustomFieldValues.CustomFieldValue", "relationships": ["CustomFieldValues", "CustomFieldValues.value"]}, + "lines": {"type": 'OrderLine', "multifield": True, "ls_field": "OrderLines.OrderLine", "relationships": ["OrderLines"]}, } class OrderLine(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "orderLineID"}, + "quantity": {"type": int, "ls_field": "quantity"}, + "price": {"type": float, "ls_field": "price"}, + "original_price": {"type": float, "ls_field": "originalPrice"}, + "count_checked_in": {"type": int, "ls_field": "checkedIn"}, + "count_received": {"type": int, "ls_field": "numReceived"}, + "total": {"type": float, "ls_field": "total"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "order": {"type": 'Order', "ls_field_id": "orderID"}, + "item": {"type": 'Item', "ls_field_id": "itemID"}, } class OrderCustomField(BaseObject): _object_attributes = { "id": {"type": int, "ls_field": "customFieldID"}, + "name": {"type": str, "ls_field": "name"}, + "type": {"type": str, "ls_field": "type"}, + "units": {"type": str, "ls_field": "uom"}, + "decimal_precision": {"type": int, "ls_field": "decimalPrecision"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "default": {"type": str, "ls_field": "default"}, # TODO: may be a mixed type, so this may not work? + } + + +class OrderCustomFieldValue(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "customFieldValueID"}, + "custom_field": {"type": 'OrderCustomField', "ls_field_id": "customFieldID"}, + "is_deleted": {"type": bool, "ls_field": "deleted"}, + "name": {"type": str, "ls_field": "name"}, + "type": {"type": str, "ls_field": "type"}, # TODO: do we want to convert this to a Python type? + "value": {"type": str, "ls_field": "value"}, # TODO: do we want to convert this to its Python type? } class OrderCustomFieldChoice(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "customFieldChoiceID"}, + "name": {"type": str, "ls_field": "name"}, + "value": {"type": str, "ls_field": "value"}, + "is_deletable": {"type": bool, "ls_field": "canBeDeleted"}, + "custom_field": {"type": 'OrderCustomField', "ls_field_id": "customFieldID"}, } \ No newline at end of file diff --git a/lightspeed_api/models/payment_type.py b/lightspeed_api/models/payment_type.py index be6e756..df99ae3 100644 --- a/lightspeed_api/models/payment_type.py +++ b/lightspeed_api/models/payment_type.py @@ -3,5 +3,11 @@ class PaymentType(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "paymentTypeID"}, + "name": {"type": str, "ls_field": "name"}, + "is_require_customer": {"type": bool, "ls_field": "requireCustomer"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "is_internal_type": {"type": bool, "ls_field": "internalReserved"}, + "id": {"type": str, "ls_field": "type"}, # TODO: enum + "refunded_payment_type": {"type": 'PaymentType', "ls_field": "refundAsPaymentTypeID"}, } \ No newline at end of file diff --git a/lightspeed_api/models/price_level.py b/lightspeed_api/models/price_level.py index 77e8c60..a56a527 100644 --- a/lightspeed_api/models/price_level.py +++ b/lightspeed_api/models/price_level.py @@ -3,5 +3,10 @@ class PriceLevel(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "priceLevelID"}, + "name": {"type": str, "ls_field": "name"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "can_be_archived": {"type": bool, "ls_field": "canBeArchived"}, + "type": {"type": int, "ls_field": "type"}, # TODO: enum + "calculation": {"type": str, "ls_field": "Calculation"}, # TODO: "reference" type? } \ No newline at end of file diff --git a/lightspeed_api/models/register.py b/lightspeed_api/models/register.py index 76c0caa..9bd9aef 100644 --- a/lightspeed_api/models/register.py +++ b/lightspeed_api/models/register.py @@ -1,17 +1,31 @@ +from datetime import datetime from . import BaseObject class Register(BaseObject): - def __init__(self, obj=None, api=None): - if obj: - self.id = obj['registerID'] - self.name = obj['name'] + _object_attributes = { + "id": {"type": int, "ls_field": "registerID"}, + "name": {"type": str, "ls_field": "name"}, + "is_open": {"type": bool, "ls_field": "open"}, + "opened_time": {"type": datetime, "ls_field": "openTime"}, + "opened_by_employee": {"type": 'Employee', "ls_field": "openEmployeeID"}, + "shop": {"type": 'Shop', "ls_field": "shopID"}, + } + _get_function = "RegistersAPI.get_register" + _update_url = 'Register/%s.json' + _create_url = 'Register.json' @property def active_sale(self): return self.api.sales.get_active_sale_at_register(self.id) + def close(self): + pass + + def open(self): + pass + def __repr__(self): return self.name @@ -21,17 +35,47 @@ def __hash__(self): class RegisterCount(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "registerCountID"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "opened_time": {"type": datetime, "ls_field": "openTime"}, + "notes": {"type": str, "ls_field": "notes"}, + "register": {"type": 'Register', "ls_field": "registerID"}, + "opened_by_employee": {"type": 'Employee', "ls_field": "openEmployeeID"}, + "closed_by_employee": {"type": 'Employee', "ls_field": "closeEmployeeID"}, + "amounts": {"type": 'RegisterCountAmount', "multifield": True, "ls_field": "RegisterCountAmounts.RegisterCountAmount", "relationships": ["RegisterCountAmounts", "RegisterCountAmounts.PaymentType"]}, } class RegisterCountAmount(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "registerCountAmountID"}, + "calculated_amount": {"type": float, "ls_field": "calculated"}, + "actual_amount": {"type": float, "ls_field": "actual"}, + "register": {"type": 'Register', "ls_field": "registerCountID"}, + "payment_type": {"type": 'PaymentType', "ls_field": "PaymentType", "ls_field_id": "paymentTypeID", "relationships": ["PaymentType"]}, } class RegisterWithdraw(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "registerWithdrawID"}, + "amount": {"type": float, "ls_field": "amount"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "notes": {"type": str, "ls_field": "notes"}, + "register": {"type": 'Register', "ls_field": "registerID"}, + "employee": {"type": 'Employee', "ls_field": "employeeID"}, + "payment_type": {"type": 'PaymentType', "ls_field": "PaymentType", "ls_field_id": "paymentTypeID", "relationships": ["PaymentType"]}, + } + + +class ReceiptSetup(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "receiptSetupID"}, + "general_message": {"type": str, "ls_field": "generalMsg"}, + "workorder_agreement": {"type": str, "ls_field": "workorderAgree"}, + "credit_card_agreement": {"type": str, "ls_field": "creditcardAgree"}, + "logo_url": {"type": str, "ls_field": "logo"}, + "logo_height": {"type": int, "ls_field": "logoHeight"}, + "logo_width": {"type": int, "ls_field": "logoWidth"}, + "header": {"type": str, "ls_field": "header"}, } \ No newline at end of file diff --git a/lightspeed_api/models/sale.py b/lightspeed_api/models/sale.py index 1b27da7..fa27089 100644 --- a/lightspeed_api/models/sale.py +++ b/lightspeed_api/models/sale.py @@ -4,55 +4,101 @@ class Sale(BaseObject): - def __init__(self, obj=None, api=None): - if obj: - self.id = obj['saleID'] - self.timestamp = obj['timeStamp'] - self.completed = obj['completed'] - self.total = obj['calcTotal'] - self.customer_id = obj['customerID'] - self.internal_note_id = "0" if "SaleNotes" not in obj or "InternalNote" not in obj["SaleNotes"] else obj["SaleNotes"]["InternalNote"]['noteID'] - self.internal_note = "" if self.internal_note_id == "0" else obj["SaleNotes"]["InternalNote"]["note"] - self.printed_note_id = "0" if "SaleNotes" not in obj or "PrintedNote" not in obj["SaleNotes"] else obj["SaleNotes"]["PrintedNote"]['noteID'] - self.printed_note = "" if self.printed_note_id == "0" else obj["SaleNotes"]["PrintedNote"]['note'] - self._obj = obj - - @property - def items(self): - return self.api.sales.get_all_from_sale(self.id) - - def has_customer(self): - return self.customer_id != "0" + _object_attributes = { + "id": {"type": int, "ls_field": "saleID"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "is_completed": {"type": bool, "ls_field": "completed"}, + "is_voided": {"type": bool, "ls_field": "voided"}, + "is_promotions_enabled": {"type": bool, "ls_field": "enablePromotions"}, + "reference_number": {"type": str, "ls_field": "referenceNumber"}, + "reference_number_source": {"type": str, "ls_field": "referenceNumberSource"}, + "tax_rate_1": {"type": float, "ls_field": "tax1Rate"}, + "tax_rate_2": {"type": float, "ls_field": "tax2Rate"}, + "change": {"type": float, "ls_field": "change"}, + "discounted": {"type": float, "ls_field": "calcDiscount"}, + "total_before_discount": {"type": float, "ls_field": "calcTotal"}, + "subtotal": {"type": float, "ls_field": "calcSubtotal"}, + "subtotal_tax": {"type": float, "ls_field": "calcTaxable"}, + "subtotal_nontax": {"type": float, "ls_field": "calcNonTaxable"}, + "average_line": {"type": float, "ls_field": "calcAvgCost"}, + "fifo_cost": {"type": float, "ls_field": "calcFIFOCost"}, + "total_tax_1": {"type": float, "ls_field": "calcTax1"}, + "total_tax_2": {"type": float, "ls_field": "calcTax2"}, + "total_payments": {"type": float, "ls_field": "calcPayments"}, + "total": {"type": float, "ls_field": "total"}, + "due": {"type": float, "ls_field": "totalDue"}, + "displayable_total": {"type": float, "ls_field": "displayableTotal"}, + "balance": {"type": float, "ls_field": "balance"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "updated_time": {"type": datetime, "ls_field": "updateTime"}, + "completed_time": {"type": datetime, "ls_field": "completeTime"}, + "customer": {"type": 'Customer', "ls_field": "Customer", "ls_field_id": "customerID", "relationships": ["Customer"]}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, + "tax_category": {"type": 'TaxCategory', "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, + "shop": {"type": 'Shop', "ls_field_id": "shopID"}, + "register": {"type": 'Shop', "ls_field_id": "registerID"}, + "items": {"type": 'SaleLine', "multifield": True, "ls_field": "SaleLines.SaleLine", "relationships": ["SaleLines"]}, + "note": {"type": 'Note', "ls_field": "Note", "relationships": ["SaleNotes"], 'optional': True}, + "payments": {"type": 'SalePayment', "multifield": True, "ls_field": "SalePayments.SalePayment", "relationships": ["SalePayments"]}, + "shipping_info": {"type": 'ShipTo', "ls_field": "ShipTo", "relationships": ["ShipTo"]}, + "quote": {"type": 'Quote', "ls_field": "Quote", "relationships": ["Quote"]}, + } + _get_function = "SalesAPI.get_sale" + _update_url = 'Sale/%s.json' + _create_url = 'Sale.json' - @property - def customer(self): - if self.customer_id == "0": - return None - return self.api.customers.get_customer(self.customer_id) - - def set_note(self, note): - return self.api.sales.set_note(self.id, note) - - -class SaleLineItem(BaseObject): - def __init__(self, obj=None, api=None): - if obj: - self.id = obj['saleLineID'] - self.sale_id = obj['saleID'] - self.item_id = obj['itemID'] - self.created_at = obj['createTime'] - self.total = obj['calcTotal'] - self.count = int(obj['unitQuantity']) - self._obj = obj + def email_receipt(self): + pass + + def refund(self): + pass + + +class SaleLine(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "saleLineID"}, + "is_layaway": {"type": bool, "ls_field": "isLayaway"}, + "is_workorder": {"type": bool, "ls_field": "isWorkorder"}, + "is_special_order": {"type": bool, "ls_field": "isSpecialOrder"}, + "is_taxed": {"type": bool, "ls_field": "tax"}, + "quantity": {"type": int, "ls_field": "unitQuantity"}, + "unit_price": {"type": float, "ls_field": "unitPrice"}, + "normal_unit_price": {"type": float, "ls_field": "normalUnitPrice"}, + "discounted": {"type": float, "ls_field": "discountAmount"}, + "discount_percent": {"type": float, "ls_field": "discountPercent"}, + "average_cost": {"type": float, "ls_field": "avgCost"}, + "fifo_cost": {"type": float, "ls_field": "fifoCost"}, + "tax_rate_1": {"type": float, "ls_field": "tax1Rate"}, + "tax_rate_2": {"type": float, "ls_field": "tax2Rate"}, + "displayed_subtotal": {"type": float, "ls_field": "displayableSubtotal"}, + "displayed_unit_price": {"type": float, "ls_field": "displayableUnitPrice"}, + "line_discounted": {"type": float, "ls_field": "calcLineDiscount"}, + "transaction_discounted": {"type": float, "ls_field": "calcTransactionDiscount"}, + "total": {"type": float, "ls_field": "calcTotal"}, + "subtotal": {"type": float, "ls_field": "calcSubtotal"}, + "total_tax_1": {"type": float, "ls_field": "calcTax1"}, + "total_tax_2": {"type": float, "ls_field": "calcTax2"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, + "tax_category": {"type": 'TaxCategory', "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, + "tax_class": {"type": 'TaxClass', "ls_field": "TaxClass", "ls_field_id": "taxClassID", "relationships": ["TaxClass"]}, + "customer": {"type": 'Customer', "ls_field_id": "customerID "}, + "shop": {"type": 'Shop', "ls_field_id": "shopID"}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "register": {"type": 'Shop', "ls_field_id": "registerID"}, + "sale": {"type": 'Sale', "ls_field_id": "saleID"}, + "parent_sale_line": {"type": 'SaleLine', "ls_field_id": "parentSaleLineID"}, + "item": {"type": 'Item', "ls_field": "Item", "ls_field_id": "itemID", "relationships": ["Item"]}, + "note": {"type": 'Note', "ls_field": "Note", "ls_field_id": "noteID", "relationships": ["Note"], 'optional': True}, + } @property def tags(self): return self.api.items.get_tags_for_item(self.item_id) - @property - def item(self): - return self.api.items.get_item(self.item_id) - @property # TODO: figure out how to pass in the object (if it existed) instead of re-looking it up. def sale(self): @@ -64,23 +110,71 @@ def set_note(self, note): class Quote(BaseObject): search_terms = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "quoteID"}, + "issue_time": {"type": datetime, "ls_field": "issueDate"}, + "notes": {"type": str, "ls_field": "notes"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "employee": {"type": 'Employee', "ls_field": "employeeID"}, + "sale": {"type": 'Sale', "ls_field": "saleID"}, } class SalePayment(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "salePaymentID"}, + "amount": {"type": float, "ls_field": "amount"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "client_reference": {"type": str, "ls_field": "remoteReference"}, + "payment_reference": {"type": str, "ls_field": "paymentID"}, + "type": {"type": 'PaymentType', "ls_field": "PaymentType", "ls_field_id": "paymentTypeID", "relationships": ["PaymentType"]}, + "credit_card_charge": {"type": 'CreditCardCharge', "ls_field": "CCCharge", "ls_field_id": "ccChargeID", "relationships": ["CCCharge"]}, + "sale": {"type": 'Sale', "ls_field": "saleID"}, + "referenced_sale_payment": {"type": 'SalePayment', "ls_field": "refPaymentID"}, + "register": {"type": 'Register', "ls_field": "registerID"}, + "credit_account": {"type": 'CreditAccount', "ls_field": "creditAccountID"}, + "employee": {"type": 'Employee', "ls_field": "employeeID"}, + "sale_account": {"type": 'SaleAccount', "ls_field": "SaleAccounts", "relationships": ["SaleAccounts"]}, + } + + +class SaleAccount(BaseObject): + _object_attributes = { + "credit_account": {"type": 'CreditAccount', "ls_field": "creditAccountID"}, + "payment": {"type": 'SalePayment', "ls_field": "salePaymentID"}, + "sale_line": {"type": 'SaleLine', "ls_field": "saleLineID"}, + } + + +class SalePaymentSignature(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "salePaymentSignatureID"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "payment": {"type": int, "ls_field": "salePaymentID"}, } class SaleVoid(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "saleVoidID"}, + "created_time": {"type": datetime, "ls_field": "createTime"}, + "reason": {"type": str, "ls_field": "reason"}, + "sale": {"type": 'Sale', "ls_field": "saleID"}, + "register": {"type": 'Register', "ls_field": "registerID"}, + "employee": {"type": 'Employee', "ls_field": "employeeID"}, } class SpecialOrder(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "specialOrderID"}, + "is_customer_contacted": {"type": bool, "ls_field": "contacted"}, + "is_completed": {"type": bool, "ls_field": "completed"}, + "quantity": {"type": int, "ls_field": "unitQuantity"}, + "customer": {"type": 'Customer', "ls_field": "customerID"}, + "shop": {"type": 'Shop', "ls_field": "shopID"}, + "transfer_item": {"type": 'InventoryTransferItem', "ls_field": "transferItemID"}, + "sale_line": {"type": 'SaleLine', "ls_field": "SaleLine", "relationships": ["SaleLine"]}, + "order_line": {"type": 'OrderLine', "ls_field": "OrderLine", "relationships": ["OrderLine"]}, + "note": {"type": 'Note', "ls_field": "Note", "relationships": ["Note"], 'optional': True}, } \ No newline at end of file diff --git a/lightspeed_api/models/serialized.py b/lightspeed_api/models/serialized.py index 8ea9898..b8b8677 100644 --- a/lightspeed_api/models/serialized.py +++ b/lightspeed_api/models/serialized.py @@ -1,7 +1,17 @@ +from datetime import datetime from . import BaseObject + class SerializedItem(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "serializedID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "color": {"type": str, "ls_field": "colorName"}, + "size": {"type": str, "ls_field": "sizeName"}, + "serial_numer": {"type": str, "ls_field": "serial"}, + "description": {"type": str, "ls_field": "description"}, + "item": {"type": 'Item', "ls_field": "itemID"}, + "sale_line": {"type": 'SaleLine', "ls_field": "saleLineID"}, + "customer": {"type": 'Customer', "ls_field": "customerID"}, } \ No newline at end of file diff --git a/lightspeed_api/models/session.py b/lightspeed_api/models/session.py index ff21ee5..6f97c00 100644 --- a/lightspeed_api/models/session.py +++ b/lightspeed_api/models/session.py @@ -3,5 +3,12 @@ class Session(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "sessionID"}, + "session_cookie": {"type": str, "ls_field": "sessionCookie"}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "account_id": {"type": int, "ls_field_id": "systemCustomerID"}, + "employee_user_id": {"type": int, "ls_field_id": "systemUserID"}, + "api_client_id": {"type": int, "ls_field_id": "systemAPIClientID"}, + "api_key_id": {"type": int, "ls_field_id": "systemAPIKeyID"}, + "shop_count": {"type": int, "ls_field_id": "shopCount"}, } \ No newline at end of file diff --git a/lightspeed_api/models/shipping.py b/lightspeed_api/models/shipping.py index 590ea75..992f9ef 100644 --- a/lightspeed_api/models/shipping.py +++ b/lightspeed_api/models/shipping.py @@ -1,7 +1,17 @@ +from datetime import datetime from . import BaseObject + class ShipTo(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "shipToID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "is_shipped": {"type": bool, "ls_field": "shipped"}, + "notes": {"type": str, "ls_field": "shipNote"}, + "first_name": {"type": str, "ls_field": "firstName"}, + "last_name": {"type": str, "ls_field": "lastName"}, + "customer": {"type": 'Customer', "ls_field_id": "customerID"}, + "sale": {"type": 'Sale', "ls_field_id": "saleID"}, + "shipping_address": {"type": 'Contact', "ls_field": "Contact", "relationships": ["Contact"]}, # TODO: contact mapping be different } \ No newline at end of file diff --git a/lightspeed_api/models/shop.py b/lightspeed_api/models/shop.py index 2ab3989..18a11e4 100644 --- a/lightspeed_api/models/shop.py +++ b/lightspeed_api/models/shop.py @@ -3,5 +3,18 @@ class Shop(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "shopID"}, + "name": {"type": str, "ls_field": "name"}, + "labor_rate": {"type": float, "ls_field": "serviceRate"}, + "timezone": {"type": str, "ls_field": "timeZone"}, # TODO: enum? + "is_labor_taxed": {"type": bool, "ls_field": "taxLabor"}, + "product_title_label": {"type": str, "ls_field": "labelTitle"}, # TODO: ENUM + "is_msrp_on_label": {"type": bool, "ls_field": "labelMsrp"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "contact": {"type": 'Contact', "ls_field": "Contact", "ls_field_id": "contactID", "relationships": ["Contact"]}, # TODO: implement contact + "tax_category": {"type": 'TaxCategory', "ls_field": "TaxCategory", "ls_field_id": "taxCategoryID", "relationships": ["TaxCategory"]}, + "receipt_setup": {"type": 'ReceiptSetup', "ls_field": "ReceiptSetup", "ls_field_id": "receiptSetupID", "relationships": ["ReceiptSetup"]}, + "credit_card_gateway": {"type": 'CreditCardGateway', "ls_field": "CCGateway", "ls_field_id": "ccGatewayID", "relationships": ["PriceLevel"]}, + "price_level": {"type": 'PriceLevel', "ls_field": "PriceLevel", "ls_field_id": "priceLevelID", "relationships": ["CCGateway"]}, + "registers": {"type": 'Register', "multifield": True, "ls_field": "Registers.Register", "relationships": ["Registers"]}, } \ No newline at end of file diff --git a/lightspeed_api/models/tag.py b/lightspeed_api/models/tag.py index 5873aab..6138cce 100644 --- a/lightspeed_api/models/tag.py +++ b/lightspeed_api/models/tag.py @@ -31,6 +31,7 @@ def __eq__(self, item): # TODO: explore this a bit more before we go do it +# Unsure of what it will do/have (it's an XML format?) class TagGroup(BaseObject): _object_attributes = { "id": {"type": int, "ls_field": "customFieldID"}, diff --git a/lightspeed_api/models/tax.py b/lightspeed_api/models/tax.py index c4c9aee..652a688 100644 --- a/lightspeed_api/models/tax.py +++ b/lightspeed_api/models/tax.py @@ -1,19 +1,30 @@ +from datetime import datetime from . import BaseObject class TaxCategory(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "taxCategoryID"}, + "is_tax_inclusive": {"type": bool, "ls_field": "isTaxInclusive"}, + "tax_name_1": {"type": str, "ls_field": "tax1Name"}, + "tax_name_2": {"type": str, "ls_field": "tax2Name"}, + "tax_rate_1": {"type": float, "ls_field": "tax1Rate"}, + "tax_rate_2": {"type": float, "ls_field": "tax2Rate"}, + "tax_classes": {"type": 'TaxCategoryClass', "multifield": True, "ls_field": "TaxCategoryClasses.TaxCategoryClass", "relationships": ["TaxCategoryClasses", "TaxCategoryClasses.TaxClass"]}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, } class TaxCategoryClass(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "taxCategoryClassID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, } class TaxClass(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "taxClassID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "name": {"type": str, "ls_field": "name"}, } \ No newline at end of file diff --git a/lightspeed_api/models/vendor.py b/lightspeed_api/models/vendor.py new file mode 100644 index 0000000..dc32bc2 --- /dev/null +++ b/lightspeed_api/models/vendor.py @@ -0,0 +1,38 @@ + +from datetime import datetime +from . import BaseObject + +class VendorRepresentative(BaseObject): + _object_attributes = { + "first_name": {"type": str, "ls_field": "firstName"}, + "last_name": {"type": str, "ls_field": "lastName"}, + "name": {"combine": ["first_name", "last_name"]}, + } + +class Vendor(BaseObject): + _object_attributes = { + "id": {"type": int, "ls_field": "vendorID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "name": {"type": str, "ls_field": "name"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "account_number": {"type": str, "ls_field": "accountNumber"}, + "price_level": {"type": str, "ls_field": "priceLevel"}, + "is_update_price": {"type": bool, "ls_field": "updatePrice"}, + "is_update_cost": {"type": bool, "ls_field": "updateCost"}, + "is_update_description": {"type": bool, "ls_field": "updateDescription"}, + "is_sell_through_data_enabled": {"type": bool, "ls_field": "shareSellThrough"}, + "contact": {"type": 'Contact', "optional": True, "ls_field": "Contact", "relationships": ["Contact"]}, + "representatives": {"type": 'VendorRepresentative', "multifield": True, "ls_field": "Reps.VendorRep"}, + } + _get_function = "VendorsAPI.get_vendor" + _update_url = 'Vendor/%s.json' + _create_url = 'Vendor.json' + + def delete(self): + self.api.request('DELETE', f'Vendor/{self.id}.json') + + def __repr__(self): + return self.name + + def __hash__(self): + return hash(self.name) \ No newline at end of file diff --git a/lightspeed_api/models/workorder.py b/lightspeed_api/models/workorder.py index 5b75b24..bbaa0ef 100644 --- a/lightspeed_api/models/workorder.py +++ b/lightspeed_api/models/workorder.py @@ -1,25 +1,87 @@ +from datetime import datetime from . import BaseObject class Workorder(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "workorderID"}, + "received_time": {"type": datetime, "ls_field": "timeIn"}, + "estimated_out_time": {"type": datetime, "ls_field": "etaOut"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "note": {"type": str, "ls_field": "note"}, + "is_warrantied": {"type": bool, "ls_field": "warranty"}, + "is_taxed": {"type": bool, "ls_field": "tax"}, + "is_archived": {"type": bool, "ls_field": "archived"}, + "hook_in": {"type": str, "ls_field": "hookIn"}, + "hook_out": {"type": str, "ls_field": "hookOut"}, + "is_saving_parts": {"type": bool, "ls_field": "saveParts"}, + "is_same_employee_for_all_lines": {"type": bool, "ls_field": "assignEmployeeToAll"}, + "customer": {"type": 'Customer', "ls_field": "Customer", "ls_field_id": "customerID", "relationships": ["Customer"]}, + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, + "employee": {"type": 'Employee', "ls_field": "Employee", "ls_field_id": "employeeID", "relationships": ["Employee"]}, + "serialized_object": {"type": 'Serialized', "ls_field": "Serialized", "ls_field_id": "serializedID", "relationships": ["Serialized"]}, + "shop": {"type": 'Shop', "ls_field_id": "shopID"}, + "sale": {"type": 'Sale', "ls_field_id": "saleID"}, + "sale_line": {"type": 'SaleLine', "ls_field_id": "saleLineID"}, + "status": {"type": 'WorkorderStatus', "ls_field": "WorkorderStatus", "ls_field_id": "workorderStatusID", "relationships": ["WorkorderStatus"]}, + "items": {"type": 'WorkorderItem', "multifield": True, "ls_field": "WorkorderItems.WorkorderItem", "relationships": ["WorkorderItems"]}, + "lines": {"type": 'WorkorderLine', "multifield": True, "ls_field": "WorkorderLines.WorkorderLine", "relationships": ["WorkorderLines"]}, } + _get_function = "WorkordersAPI.get_workorder" class WorkorderItem(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "workorderItemID"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "quantity": {"type": int, "ls_field": "unitQuantity"}, + "price": {"type": float, "ls_field": "unitPrice"}, + "is_approved": {"type": bool, "ls_field": "approved"}, + "is_warrantied": {"type": bool, "ls_field": "warranty"}, + "is_taxed": {"type": bool, "ls_field": "tax"}, + "is_special_order": {"type": bool, "ls_field": "isSpecialOrder"}, + "note": {"type": str, "ls_field": "note"}, + "workorder": {"type": 'Workorder', "ls_field_id": "workorderID"}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "sale_line": {"type": 'SaleLine', "ls_field_id": "saleLineID"}, + "sale": {"type": 'Sale', "ls_field_id": "saleID"}, + "item": {"type": 'Item', "ls_field_id": "itemID"}, + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, } + _get_function = "WorkordersAPI.get_workorder_item" class WorkorderLine(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, + "id": {"type": int, "ls_field": "workorderLineID"}, + "note": {"type": str, "ls_field": "note"}, + "last_modified_time": {"type": datetime, "ls_field": "timeStamp"}, + "hours": {"type": int, "ls_field": "hours"}, + "minutes": {"type": int, "ls_field": "minutes"}, + "price_override": {"type": float, "ls_field": "unitPriceOverride"}, + "quantity": {"type": int, "ls_field": "unitQuantity"}, + "cost": {"type": float, "ls_field": "unitCost"}, + "is_done": {"type": bool, "ls_field": "done"}, + "is_approved": {"type": bool, "ls_field": "approved"}, + "is_warrantied": {"type": bool, "ls_field": "warranty"}, + "is_taxed": {"type": bool, "ls_field": "tax"}, + "workorder": {"type": 'Workorder', "ls_field_id": "workorderID"}, + "employee": {"type": 'Employee', "ls_field_id": "employeeID"}, + "sale_line": {"type": 'SaleLine', "ls_field_id": "saleLineID"}, + "sale": {"type": 'Sale', "ls_field_id": "saleID"}, + "item": {"type": 'Item', "ls_field_id": "itemID"}, + "discount": {"type": 'Discount', "ls_field": "Discount", "ls_field_id": "discountID", "relationships": ["Discount"]}, + "tax_class": {"type": 'TaxClass', "ls_field": "TaxClass", "ls_field_id": "taxClassID", "relationships": ["TaxClass"]}, } + _get_function = "WorkordersAPI.get_workorder_line" class WorkorderStatus(BaseObject): _object_attributes = { - "id": {"type": int, "ls_field": "customFieldID"}, - } \ No newline at end of file + "id": {"type": int, "ls_field": "workorderStatusID"}, + "name": {"type": str, "ls_field": "name"}, + "sort_order": {"type": int, "ls_field": "sortOrder"}, + "color": {"type": str, "ls_field": "htmlColor"}, + "system_value": {"type": str, "ls_field": "systemValue"}, + } + _get_function = "WorkordersAPI.get_workorder_status" diff --git a/lightspeed_api/utils.py b/lightspeed_api/utils.py index 25c9e06..c0362f6 100644 --- a/lightspeed_api/utils.py +++ b/lightspeed_api/utils.py @@ -19,6 +19,11 @@ def convert_to_type(obj_type, obj): return int(obj) except ValueError: raise Exception("The value %s could not be converted to a number" % obj) + elif obj_type == float: + try: + return float(obj) + except ValueError: + raise Exception("The value %s could not be converted to a floating-point integer" % obj) elif obj_type == datetime: try: dtobj = datetime.strptime(obj, LIGHTSPEED_DATETIME_FORMAT) @@ -41,6 +46,10 @@ def convert_from_type(obj_type, obj): return str(obj) # Done to force conversion of Object to str if applicable elif obj_type == int: return str(obj) + elif obj_type == float: + return str(obj) + elif obj_type == bool: + return "true" if obj else "false" elif obj_type in [datetime, date]: try: dtobj = datetime.strftime(obj, LIGHTSPEED_DATETIME_FORMAT) diff --git a/tests/__pycache__/__init__.cpython-39.pyc b/tests/__pycache__/__init__.cpython-39.pyc index f4ad5107df153b91e7750626168d8e6188389a58..0e6ab34c278a236386fbc065502e85c6befbd30b 100644 GIT binary patch delta 19 ZcmbQwIG>R_k(ZZ?0SLsW^iAZR0stmD1Udi! delta 19 ZcmbQwIG>R_k(ZZ?0SF|rYbSC~0RSa01OWg5 diff --git a/tests/__pycache__/conftest.cpython-39-pytest-6.2.3.pyc b/tests/__pycache__/conftest.cpython-39-pytest-6.2.3.pyc index 5ff722ab3b6bc741949c88d84e680497cabdbd5a..880c111249d4183e358978ca91ae4dcc9405d15b 100644 GIT binary patch delta 20 acmaFP`ka+Jk(ZZ?0SLsW^ljw6!vp{}#sxe8 delta 20 acmaFP`ka+Jk(ZZ?0SJ1tYd3P=VFCa+wFP(p diff --git a/tests/__pycache__/test_customers.cpython-39-pytest-6.2.3.pyc b/tests/__pycache__/test_customers.cpython-39-pytest-6.2.3.pyc index 9c8c9e2b4b9efbe0c1c97cbd5381109a07197143..2e206d86ddb535814be0ee81ef8a772693ae2627 100644 GIT binary patch literal 25709 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

C_Dmo@blj>BRW2gQruT+EF|c!1J6BPnYfYDC>5j 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 AUH||9 literal 24791 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 qoC6{>Av;O9@JQ2IBw# diff --git a/tests/test_customers.py b/tests/test_customers.py index 5fddb13..bbb85c6 100644 --- a/tests/test_customers.py +++ b/tests/test_customers.py @@ -1,5 +1,6 @@ -from datetime import datetime +import json +from datetime import datetime, timedelta import lightspeed_api @@ -42,15 +43,16 @@ def test_customer_create_complex(ls_client): customer.company = "Home Depot" customer.type = ls_client.api.customers.get_customer_type(1) + customer.contact = lightspeed_api.models.Contact() email1 = lightspeed_api.models.CustomerEmail("tst@hdepot.com", lightspeed_api.models.CustomerEmailType.PRIMARY) email2 = lightspeed_api.models.CustomerEmail("someotheremail@fakenews.com", lightspeed_api.models.CustomerEmailType.SECONDARY) - customer.emails = [email1, email2] + customer.contact.emails = [email1, email2] phone1 = lightspeed_api.models.CustomerPhoneNumber("+16138311828", lightspeed_api.models.CustomerPhoneNumberType.WORK) phone2 = lightspeed_api.models.CustomerPhoneNumber("6131234567", lightspeed_api.models.CustomerPhoneNumberType.FAX) - customer.phone_numbers = [phone1, phone2] + customer.contact.phone_numbers = [phone1, phone2] - customer.website = "www.google.com" + customer.contact.website = "www.google.com" address1 = lightspeed_api.models.CustomerAddress() address1.address_line_1 = "123 Fake St" @@ -59,7 +61,7 @@ def test_customer_create_complex(ls_client): address1.state = "Ontario" address1.country = "Canada" address1.zipcode = "A1A 1A1" - customer.addresses.append(address1) + customer.contact.addresses.append(address1) t1 = lightspeed_api.models.Tag() t1.name = "fullprofile" @@ -85,10 +87,10 @@ def test_customer_create_complex(ls_client): #assert(customer.birthday == customer_copy.birthday) assert(customer.company == customer_copy.company) assert(customer.type == customer_copy.type) - assert(customer.emails == customer_copy.emails) + assert(customer.contact.emails == customer_copy.contact.emails) assert(customer.tags == customer_copy.tags) assert(customer.note == customer_copy.note) - assert(customer_copy.note.last_modified_time < datetime.utcnow()) + assert(customer_copy.note.last_modified_time < (datetime.utcnow() + timedelta(seconds=10))) # time on server may be skewed a few seconds except AssertionError as ex: customer.delete() # Delete if things failed. raise ex @@ -139,15 +141,15 @@ def test_customer_get_created_from_ls(ls_client): assert(customer.type is not None) assert(customer.type.id == 1) - assert(len(customer.emails) == 2) - for t in customer.emails: + assert(len(customer.contact.emails) == 2) + for t in customer.contact.emails: assert(t is not None) assert(t.address is not None) assert(t.type == lightspeed_api.models.CustomerEmailType.PRIMARY or t.type == lightspeed_api.models.CustomerEmailType.SECONDARY) - assert(len(customer.phone_numbers) == 5) - for t in customer.phone_numbers: + assert(len(customer.contact.phone_numbers) == 5) + for t in customer.contact.phone_numbers: assert(t is not None) assert(t.number is not None) if t.number == "1111111111": @@ -163,8 +165,8 @@ def test_customer_get_created_from_ls(ls_client): else: assert(False) - assert(len(customer.addresses) == 1) - for t in customer.addresses: + assert(len(customer.contact.addresses) == 1) + for t in customer.contact.addresses: assert(t is not None) assert(t.address_line_1 == "1 Wellington St") assert(t.address_line_2 == "ATTN: Someone") @@ -177,7 +179,7 @@ def test_customer_get_created_from_ls(ls_client): for t in customer.tags: assert(t.id) - assert(customer.website == "www.canada.ca") + assert(customer.contact.website == "www.canada.ca") assert(customer.credit_account is not None) assert(customer.discount is not None) assert(customer.discount.id == 1) @@ -207,6 +209,13 @@ def test_customer_get_created_from_ls(ls_client): assert(customer.name == "Richard Desmarais") assert(customer.company == "Dayna's Pet Sitting") + # Make sure what we get in JSON format is the same as we expect to send back + raw_obj = ls_client.api.customers.get_customer(customer.id, raw=True) + raw_local_obj = json.loads(customer.json()) + #for f in raw_obj: + #assert(f in raw_local_obj) + #assert(raw_obj[f] == raw_local_obj[f]) + def test_customer_check_types_from_ls(ls_client): # TODO: write test diff --git a/tests/test_tags.py b/tests/test_tags.py index 8d172a5..2ced94d 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -1,6 +1,6 @@ def test_get_tags_premade(ls_client): tag_list = ls_client.api.tags.all() - assert(len(tag_list) == 4) + assert(len(tag_list) == 5) for t in tag_list: assert(t.id) diff --git a/tests/test_vendors.py b/tests/test_vendors.py new file mode 100644 index 0000000..1e29778 --- /dev/null +++ b/tests/test_vendors.py @@ -0,0 +1,53 @@ + +from datetime import datetime +import lightspeed_api + + +def test_vendor_create(ls_client): + vendor = lightspeed_api.models.Vendor() + vendor.name = "Fake Vendor" + ls_client.api.add(vendor) + vendor.save() + assert(vendor.id != None) + + id = vendor.id + vendor.account_number = "123456789" + vendor.save() + assert(vendor.id == id) + assert(vendor.name == "Fake Vendor") + assert(vendor.account_number == "123456789") + + vendor.delete() + vendor_list = ls_client.api.vendors.all(id=id) + assert(len(vendor_list) == 0) + + +def test_vendor_create_invalid(ls_client): + vendor = lightspeed_api.models.Vendor() + ls_client.api.add(vendor) + try: + vendor.save() + assert(False) + except Exception as ex: + pass # Success case + + +def test_vendor_all_functions(ls_client): + obj_list = ls_client.api.vendors.all() + assert(len(obj_list) == 9) + + +def test_vendor_get_functions(ls_client): + obj = ls_client.api.vendors.get_vendor(1) + assert(obj is not None) + + +def test_vendor_get_created_from_ls(ls_client): + vendor = ls_client.api.vendors.get_vendor(1) + assert(vendor is not None) + + assert(len(vendor.representatives) == 1) + for t in vendor.representatives: + assert(t is not None) + assert(isinstance(t, lightspeed_api.models.VendorRepresentative)) + assert(t.name=="John Doe") diff --git a/tests/test_workorders.py b/tests/test_workorders.py new file mode 100644 index 0000000..2b01b2f --- /dev/null +++ b/tests/test_workorders.py @@ -0,0 +1,182 @@ + +from datetime import datetime +import lightspeed_api + + +def test_workorder_create_delete(ls_client): + workorder = lightspeed_api.models.Workorder() + workorder.received_time = datetime.utcnow() + ls_client.api.add(workorder) + + try: + workorder.save() + assert(False) + except Exception: + pass + + workorder = ls_client.api.workorders.get_workorder(1) + try: + workorder.delete() + assert(False) + except Exception: + pass + + +def test_workorder_item_create_delete(ls_client): + workorder = lightspeed_api.models.WorkorderItem() + workorder.workorder = ls_client.api.workorders.get_workorder(1) + workorder.price = 1.0 + workorder.quantity = 1 + ls_client.api.add(workorder) + + try: + workorder.save() + assert(False) + except Exception: + pass + + workorder = ls_client.api.workorders.get_workorder_item(1) + try: + workorder.delete() + assert(False) + except Exception: + pass + + +def test_workorder_line_create_delete(ls_client): + workorder = lightspeed_api.models.WorkorderLine() + workorder.workorder = ls_client.api.workorders.get_workorder(1) + workorder.cost = 100.0 + workorder.quantity = 1 + workorder.hours = 1 + ls_client.api.add(workorder) + + try: + workorder.save() + assert(False) + except Exception: + pass + + workorder = ls_client.api.workorders.get_workorder_line(1) + try: + workorder.delete() + assert(False) + except Exception: + pass + + +def test_workorder_all_functions(ls_client): + obj_list = ls_client.api.workorders.all() + assert(len(obj_list) > 0) + + obj_list = ls_client.api.workorders.all_items() + assert(len(obj_list) > 0) + + obj_list = ls_client.api.workorders.all_lines() + assert(len(obj_list) > 0) + + obj_list = ls_client.api.workorders.all_statuses() + assert(len(obj_list) > 0) + + +def test_workorder_get_functions(ls_client): + obj = ls_client.api.workorders.get_workorder(1) + assert(obj is not None) + + obj = ls_client.api.workorders.get_workorder_item(1) + assert(obj is not None) + + obj = ls_client.api.workorders.get_workorder_line(1) + assert(obj is not None) + + obj = ls_client.api.workorders.get_workorder_status(1) + assert(obj is not None) + + +def test_workorder_get_created_from_ls(ls_client): + workorder = ls_client.api.workorders.get_workorder(1) + assert(workorder is not None) + assert(workorder.received_time is not None and workorder.received_time < datetime.utcnow()) + assert(workorder.estimated_out_time is not None and workorder.received_time < datetime.utcnow()) + assert(workorder.last_modified_time is not None and workorder.received_time < datetime.utcnow()) + assert(workorder.note == "Receipt note to be added here!") + assert(not workorder.is_warrantied) + assert(not workorder.is_taxed) + assert(not workorder.is_archived) + assert(workorder.hook_in == "2") + assert(workorder.hook_out == "") + assert(workorder.is_saving_parts) + assert(not workorder.is_same_employee_for_all_lines) + assert(workorder.customer is not None) + assert(workorder.customer.id == 285) + assert(workorder.customer.name == "Jim Bob") + assert(workorder.discount is None) + assert(workorder.employee is not None) + assert(workorder.serialized_object is None) + assert(workorder.shop is not None) + assert(workorder.sale is None) + assert(workorder.sale_line is not None) + assert(workorder.status is not None) + + assert(len(workorder.items) == 1) + for t in workorder.items: + assert(t is not None) + assert(t.last_modified_time is not None and t.last_modified_time < datetime.utcnow()) + assert(t.item is not None and t.item.id != 0) + + assert(len(workorder.lines) == 1) + for t in workorder.lines: + assert(t is not None) + assert(t.last_modified_time is not None and t.last_modified_time < datetime.utcnow()) + assert(t.sale_line is not None and t.sale_line.id != 0) + + +def test_workorder_item_created_from_ls(ls_client): + workorder = ls_client.api.workorders.get_workorder_item(1) + assert(workorder is not None) + assert(workorder.last_modified_time is not None and workorder.last_modified_time < datetime.utcnow()) + assert(workorder.note == "") + assert(workorder.is_warrantied) + assert(not workorder.is_approved) + assert(workorder.is_taxed) + assert(workorder.is_special_order) + assert(workorder.workorder is not None) + assert(workorder.workorder.id == 1) + assert(workorder.discount is None) + assert(workorder.item is not None) + assert(workorder.sale is None) + assert(workorder.employee is not None) + assert(workorder.sale_line is not None) + + +def test_workorder_line_created_from_ls(ls_client): + workorder = ls_client.api.workorders.get_workorder_line(1) + assert(workorder is not None) + assert(workorder.last_modified_time is not None and workorder.last_modified_time < datetime.utcnow()) + assert(workorder.note == "Labor") + assert(workorder.hours == 1) + assert(workorder.minutes == 0) + assert(workorder.price_override == 0.0) + assert(workorder.quantity == 1) + assert(workorder.cost == 125.0) + assert(not workorder.is_warrantied) + assert(not workorder.is_approved) + assert(not workorder.is_taxed) + assert(not workorder.is_done) + assert(workorder.workorder is not None) + assert(workorder.workorder.id == 1) + assert(workorder.discount is None) + assert(workorder.item is None) + assert(workorder.sale is None) + assert(workorder.employee is not None) + assert(workorder.sale_line is not None) + assert(workorder.tax_class is not None) + + +def test_workorder_status_created_from_ls(ls_client): + workorder = ls_client.api.workorders.get_workorder_status(1) + assert(workorder is not None) + assert(workorder.name == "Open") + assert(workorder.sort_order == 0) + assert(workorder.color == "") + assert(workorder.system_value == "open")