From 60e6613a6d00af73b88cece096b4ae146fdfd852 Mon Sep 17 00:00:00 2001 From: Daniel Coull Date: Sat, 30 Dec 2017 01:01:20 +0000 Subject: [PATCH 1/4] fix issues with catalog product create/detlete/update --- magento2/catalog.py | 74 ++++++++++++--------------------------------- magento2/rest.py | 5 --- 2 files changed, 19 insertions(+), 60 deletions(-) diff --git a/magento2/catalog.py b/magento2/catalog.py index 38150ab..2f6e4db 100644 --- a/magento2/catalog.py +++ b/magento2/catalog.py @@ -283,7 +283,7 @@ def info(self, product, store_view=None): } ) - def create(self, product_type, attribute_set_id, sku, data): + def create(self, type_id, attribute_set_id, sku, data): """ Create Product and return ID @@ -293,17 +293,18 @@ def create(self, product_type, attribute_set_id, sku, data): :param data: Dictionary of data :return: INT id of product created """ - return int(self.post( + return self.post( 'products', { - 'data': data, - 'product_type': product_type, - 'attribute_set_id': attribute_set_id, - 'sku': sku, - }) + 'product': { + 'type_id': type_id, + 'attribute_set_id': attribute_set_id, + 'sku': sku, + }.update(data) + } ) - def update(self, product, data, store_view=None, identifierType=None): + def update(self, sku, data): """ Update product Information @@ -315,52 +316,15 @@ def update(self, product, data, store_view=None, identifierType=None): :return: Boolean """ - return bool(self.call( - 'catalog_product.update', - [product, data, store_view, identifierType] - )) - - def setSpecialPrice(self, product, special_price=None, - from_date=None, to_date=None, store_view=None, - identifierType=None): - """ - Update product's special price - - :param product: ID or SKU of product - :param special_price: Special Price - :param from_date: From date - :param to_date: To Date - :param store_view: ID or Code of Store View - :param identifierType: Defines whether the product or SKU value is - passed in the "product" parameter. - - :return: Boolean - """ - return bool(self.call( - 'catalog_product.setSpecialPrice', [ - product, special_price, from_date, to_date, store_view, - identifierType - ] - )) - - def getSpecialPrice(self, product, store_view=None, identifierType=None): - """ - Get product special price data - - :param product: ID or SKU of product - :param store_view: ID or Code of Store view - :param identifierType: Defines whether the product or SKU value is - passed in the "product" parameter. - - :return: Dictionary - """ - return self.call( - 'catalog_product.getSpecialPrice', [ - product, store_view, identifierType - ] + return self.put( + 'products/%s' % sku, + { + 'product': { + }.update(data) + } ) - def delete(self, product, identifierType=None): + def remove(self, sku): """ Delete a product @@ -370,9 +334,9 @@ def delete(self, product, identifierType=None): :return: Boolean """ - return bool(self.call('catalog_product.delete', [ - product, identifierType - ])) + return self.delete( + 'products/%s' % sku, {} + ) class ProductAttribute(API): diff --git a/magento2/rest.py b/magento2/rest.py index 4784173..91b972e 100644 --- a/magento2/rest.py +++ b/magento2/rest.py @@ -38,7 +38,6 @@ def get(self, resource_path, arguments): res = requests.get( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() def post(self, resource_path, arguments): @@ -46,7 +45,6 @@ def post(self, resource_path, arguments): res = requests.post( url, data=json.dumps(arguments), verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token, "Content-Type": "application/json"}) - res.raise_for_status() return res.json() def put(self, resource_path, arguments): @@ -54,7 +52,6 @@ def put(self, resource_path, arguments): res = requests.put( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() def delete(self, resource_path, arguments): @@ -62,7 +59,6 @@ def delete(self, resource_path, arguments): res = requests.delete( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() def patch(self, resource_path, arguments): @@ -70,5 +66,4 @@ def patch(self, resource_path, arguments): res = requests.patch( url, params=arguments, verify=self._verify_ssl, headers={'Authorization': 'Bearer %s' % self._token}) - res.raise_for_status() return res.json() From 8a07acf6473d21e797083855edbb034528140ee3 Mon Sep 17 00:00:00 2001 From: Daniel Coull Date: Sat, 30 Dec 2017 01:28:15 +0000 Subject: [PATCH 2/4] fix issues arround product attributes --- magento2/api.py | 6 +-- magento2/catalog.py | 96 ++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/magento2/api.py b/magento2/api.py index 02f3222..40bceb0 100644 --- a/magento2/api.py +++ b/magento2/api.py @@ -201,7 +201,7 @@ def __exit__(self, type, value, traceback): self.client.service.endSession(self.session) self.session = None - def get(self, resource_path, arguments): + def get(self, resource_path, arguments={}): """ Proxy for REST call API """ @@ -209,7 +209,7 @@ def get(self, resource_path, arguments): raise NotImplementedError("Currently only supports REST API") return self.client.get(resource_path, arguments) - def post(self, resource_path, arguments): + def post(self, resource_path, arguments={}): """ Proxy for REST call API """ @@ -217,7 +217,7 @@ def post(self, resource_path, arguments): raise NotImplementedError("Currently only supports REST API") return self.client.post(resource_path, arguments) - def put(self, resource_path, arguments): + def put(self, resource_path, arguments={}): """ Proxy for REST call API """ diff --git a/magento2/catalog.py b/magento2/catalog.py index 2f6e4db..e21a65f 100644 --- a/magento2/catalog.py +++ b/magento2/catalog.py @@ -232,15 +232,6 @@ class Product(API): """ __slots__ = () - def currentStore(self, store_view=None): - """ - Set/Get current store view - - :param store_view: Store view ID or Code - :return: int - """ - args = [store_view] if store_view else [] - return int(self.call('catalog_product.currentStore', args)) def list(self, filters=None, store_view=None): """ @@ -337,24 +328,12 @@ def remove(self, sku): return self.delete( 'products/%s' % sku, {} ) - - class ProductAttribute(API): """ Product Attribute API """ __slots__ = () - def currentStore(self, store_view=None): - """ - Set/Get current store view - - :param store_view: Store view ID or Code - :return: int - """ - args = [store_view] if store_view else [] - return int(self.call('catalog_product_attribute.currentStore', args)) - def list(self, attribute_set_id): """ Retrieve product attribute list @@ -362,76 +341,93 @@ def list(self, attribute_set_id): :param attribute_set_id: ID of attribute set :return: `list` of `dict` """ - return self.call('catalog_product_attribute.list', [attribute_set_id]) + return self.get('products/attribute-sets/%s/attributes' % attribute_set_id, {}) - def info(self, attribute): + def info(self, attributeCode): """ Retrieve product attribute info :param attribute: ID or Code of the attribute :return: `list` of `dict` """ - return self.call('catalog_product_attribute.info', [attribute]) + return self.get('products/attributes/%s' % attributeCode) - def options(self, attribute, store_view=None): + def options(self, attribute): """ Retrieve product attribute options - :param attribute: ID or Code of the attribute + :param attribute: Code of the attribute :return: `list` of `dict` """ - return self.call('catalog_product_attribute.options', - [attribute, store_view]) + return self.get('products/attributes/%s/options' % attribute) - def addOption(self, attribute, data): + def addOption(self, attribute, label,value, data): """ Create new options to attribute (Magento > 1.7.0) - :param attribute: ID or Code of the attribute. + :param attribute: Code of the attribute. :param data: Dictionary of Data. - {'label':[{'store_id':[0,1], 'value':'Value'},], 'order':1, 'is_default':1} :return: True if created. """ - return bool(self.call('product_attribute.addOption', - [attribute, data])) + return self.post('products/attributes/%s/options' % attribute, { + 'option': { + 'label': label, + 'value': value, + }.update(data) + }) - def createOption(self, *a, **kw): - warnings.warn( - "ProductAttribute: createOption is deprecated, use addOption instead." - ) - return self.addOption(*a, **kw) - def removeOption(self, attribute, option): + def removeOption(self, attributeCode, optionId): """ Remove option to attribute (Magento > 1.7.0) - :param attribute: ID or Code of the attribute. - :param option: Option ID. + :param attributeCode: Code of the attribute. + :param optionId: Option ID. :return: True if the option is removed. """ - return bool(self.call('product_attribute.removeOption', - [attribute, option])) + return self.delete('products/attributes/%s/options/%s' % (attributeCode, optionId)) - def create(self, data): + def create(self, attribute_code, label, frontendinput, data): """ Create attribute entity. :param data: Dictionary of entity data to create attribute with. + :param attribute_code: Code for new attribute. + :param label: Label for new attribute. + :param frontendinput: Frontend Input type for new attribute use getTypes for a list of available. :return: Integer ID of attribute created """ - return self.call('catalog_product_attribute.create', [data]) + return self.post('products/attributes', + { + 'attribute': { + 'attribute_code': attribute_code, + 'default_frontend_label': label, + 'frontend_input': frontendinput + }.update(data) + }) - def update(self, attribute, data): + def update(self, attributeCode, data): """ Update attribute entity data. - :param attribute: ID or Code of the attribute. + :param attribute: Code of the attribute. :param data: Dictionary of entity data to update on attribute. - :return: Boolean + :return: Dictionary + """ + return self.post('products/attributes/%s' % attributeCode, + { + 'attribute': { + }.update(data) + }) + + def getTypes(self): + """ + Get attribute frontend types. + :return: Dictionary """ - return self.call('catalog_product_attribute.update', [attribute, data]) + return self.get('products/attributes/types',{}) class ProductAttributeSet(API): @@ -834,7 +830,7 @@ def list(self, products): :param products: list of IDs or SKUs of products :return: `list` of `dict` """ - return self.call('cataloginventory_stock_item.list', [products]) + return self.get('stockItems') def update(self, product, data): """ From 942793c646860e3307f3267f8f3e3b18f3b2b2e9 Mon Sep 17 00:00:00 2001 From: Daniel Coull Date: Sat, 20 Jan 2018 20:15:08 +0000 Subject: [PATCH 3/4] further catalog modifications --- __init__.py | 0 magento2/__init__.pyc | Bin 1603 -> 0 bytes magento2/api.py | 6 +-- magento2/catalog.py | 116 ++++++++++++++++++++++++++++++++-------- test/test.py => test.py | 4 +- 5 files changed, 101 insertions(+), 25 deletions(-) create mode 100644 __init__.py delete mode 100644 magento2/__init__.pyc rename test/test.py => test.py (96%) diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/magento2/__init__.pyc b/magento2/__init__.pyc deleted file mode 100644 index ac28b82d3f97b47c35c58c5f36b9f708a6de3056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1603 zcmZux>u%dN6#i@{PVCr;b8~J^q9$qUUbX=HSq#I{EGPnWX+Rglz(0cO!`ebc$&ly* zd5AsO-eT{t7Z?wzxb`*)JmUGzg*@LORr|Z${44+S_aVyX*nWSuqxho&5Fo9rfU2Mx zXbH3os)JTQtDpv`30ecSKyA=EXam#%ZGyHy+n^oLE~pFI1MPzjK!>0sP!DvB(J`dW zd;&T}QiZNT)=*(RK(YkA47m(lhpa=dK(2V(6K7k6T!n5xHlUl3O>a9xvIgCPY(cjn z+a5hfvJSlgxdGjQ>_BfqZhG4Vk}c?M$ZhBy$Q_S9MY0Rsh3rD_LGF3_n{9U z51EHz8)8s)|`%Zc@wSiJS9KCZG>0{78_wso1Ao@X>2 znpwbL98V^3I`Vsrj2dNj#Z4FOl`CWN_|w$*lJ3j+j`b+#-yF982OKEBSn2%nVWJAh zK?&S4@r9Yo_c2!aeQwEjn=IYl$LW{C%q?)8rJv)`G><+d$~5f4x9L}Ipxr$;;X_WA zYbKs6bQEw$tW(CBnT|00s`A3J8iujGoanOv~XDfHzM{w|Hh(c1+y|Iig#AM9NYVOIa2vJ%IPwrEW4aMm~obVSNVp_*dvt2CJeu~ q5qxfb)b^Ss>L)I-cxgqw+Nd-tbvqi>7R$ 1.7.0) @@ -369,14 +383,13 @@ def addOption(self, attribute, label,value, data): :param data: Dictionary of Data. :return: True if created. """ - return self.post('products/attributes/%s/options' % attribute, { + return self.post('products/attributes/%s/options' % (attribute,), { 'option': { 'label': label, 'value': value, }.update(data) }) - def removeOption(self, attributeCode, optionId): """ Remove option to attribute (Magento > 1.7.0) @@ -385,7 +398,7 @@ def removeOption(self, attributeCode, optionId): :param optionId: Option ID. :return: True if the option is removed. """ - return self.delete('products/attributes/%s/options/%s' % (attributeCode, optionId)) + return self.delete('products/attributes/%s/options/%s' % (attributeCode, optionId), {}) def create(self, attribute_code, label, frontendinput, data): """ @@ -416,7 +429,7 @@ def update(self, attributeCode, data): :return: Dictionary """ - return self.post('products/attributes/%s' % attributeCode, + return self.post('products/attributes/%s' % (attributeCode,), { 'attribute': { }.update(data) @@ -427,7 +440,50 @@ def getTypes(self): Get attribute frontend types. :return: Dictionary """ - return self.get('products/attributes/types',{}) + return self.get('products/attributes/types', {}) + + +class ProductAttributeGroup(API): + """ + Product Attribute Group API + """ + __slots__ = () + + def list(self, searchString=''): + """ + Retrieve list of product attribute sets + + :return: `list` of `dict` + """ + return self.get('products/attribute-sets/sets/list', []) + + def create(self, attribute_group_name, attribute_set_id, attributes={}): + """ + Create a new attribute set based on a "skeleton" attribute set. + If unsure, use the "Default" attribute set as a skeleton. + + :param attribute_set_name: name of the new attribute set + :param skeleton_set_id: id of the skeleton attribute set to base this set on. + :param attributes: dictionary of extension attributes. + + :return: Integer ID of new attribute set + """ + data = { + "group": { + "attribute_group_name": attribute_group_name, + "attribute_set_id": attribute_set_id, + "extension_attributes": attributes + } + } + return self.post('products/attribute-sets/groups', data) + + def remove(self, groupdId): + """ + Retrieve list of product attribute sets + + :return: `list` of `dict` + """ + return self.delete('products/attribute-sets/groups/%s' % (groupdId,), {}) class ProductAttributeSet(API): @@ -436,27 +492,39 @@ class ProductAttributeSet(API): """ __slots__ = () - def list(self): + def list(self, searchString=''): """ Retrieve list of product attribute sets :return: `list` of `dict` """ - return self.call('catalog_product_attribute_set.list', []) + return self.get('products/attribute-sets/sets/list', []) - def create(self, attribute_set_name, skeleton_set_id): + def create(self, attribute_set_name, skeleton_set_id, attributes={}): """ Create a new attribute set based on a "skeleton" attribute set. If unsure, use the "Default" attribute set as a skeleton. :param attribute_set_name: name of the new attribute set :param skeleton_set_id: id of the skeleton attribute set to base this set on. + :param attributes: dictionary of extension attributes. :return: Integer ID of new attribute set """ - return self.call('catalog_product_attribute_set.create', [attribute_set_name, skeleton_set_id]) - def attributeAdd(self, attribute_id, attribute_set_id): + data = { + + "attributeSet": { + "attribute_set_name": attribute_set_name, + "sort_order": 0, + "entity_type_id": 0, + "extension_attributes": attributes + }, + "skeletonId": skeleton_set_id + } + return self.post('products/attribute-sets', data) + + def attributeAdd(self, attribute_set_id, group_id, code): """ Add an existing attribute to an attribute set. @@ -465,9 +533,15 @@ def attributeAdd(self, attribute_id, attribute_set_id): :return: Boolean """ - return self.call('catalog_product_attribute_set.attributeAdd', [attribute_id, attribute_set_id]) - def attributeRemove(self, attribute_id, attribute_set_id): + data = { + "attributeSetId": attribute_set_id, + "attributeGroupId": group_id, + "attributeCode": code, + } + return self.post('products/attribute-sets/attributes', data) + + def attributeRemove(self, atribute_code, attribute_set_id): """ Remove an existing attribute to an attribute set. @@ -476,7 +550,7 @@ def attributeRemove(self, attribute_id, attribute_set_id): :return: Boolean """ - return self.call('catalog_product_attribute_set.attributeRemove', [attribute_id, attribute_set_id]) + return self.delete('products/attribute-sets/%s/attributes/%s' % (attribute_set_id, atribute_code,), {}) @@ -492,7 +566,7 @@ def list(self): :return: `list` of `dict` """ - return self.call('catalog_product_type.list', []) + return self.get('products/types', {}) class ProductImages(API): diff --git a/test/test.py b/test.py similarity index 96% rename from test/test.py rename to test.py index 7cdd475..18547b4 100644 --- a/test/test.py +++ b/test.py @@ -1,4 +1,6 @@ -import magento2 +import sys +from magento2dev import magento2 + import logging _logger = logging.getLogger(__name__) From b6a37559a4baa216deac37d1504c386ddda94609 Mon Sep 17 00:00:00 2001 From: Daniel Coull Date: Thu, 1 Feb 2018 23:08:32 +0000 Subject: [PATCH 4/4] catalog category changes --- __init__.pyc | Bin 0 -> 178 bytes magento2/__init__.pyc | Bin 0 -> 1632 bytes magento2/api.pyc | Bin 9909 -> 10401 bytes magento2/catalog.py | 44 ++++++++++++++++++++----------------- magento2/catalog.pyc | Bin 34539 -> 37621 bytes magento2/checkout.pyc | Bin 14827 -> 15552 bytes magento2/customer.pyc | Bin 5656 -> 6091 bytes magento2/directory.pyc | Bin 1599 -> 1744 bytes magento2/miscellaneous.pyc | Bin 2152 -> 2326 bytes magento2/rest.py | 4 +++- magento2/rest.pyc | Bin 3274 -> 3520 bytes magento2/sales.pyc | Bin 15804 -> 16761 bytes magento2/utils.pyc | Bin 1210 -> 0 bytes magento2/version.pyc | Bin 307 -> 336 bytes 14 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 __init__.pyc create mode 100644 magento2/__init__.pyc delete mode 100644 magento2/utils.pyc diff --git a/__init__.pyc b/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65b7979257afc9716b02184120329cbc3969850e GIT binary patch literal 178 zcmZSn%*%B;I4LTb0SXv_v;z-bJ}z|t literal 0 HcmV?d00001 diff --git a/magento2/__init__.pyc b/magento2/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed5362083d649908f208688f60bdac44ba094df0 GIT binary patch literal 1632 zcmaJ>+iu%N5IvG?*^({aE#G9Dv7JOsg9_+lP!w@ow=j@8#wzH;z7Vu_BeUU>D!DSC zkLXYK7y8iG&g{~P6ci<~!@2LwnO)ZYZZ-e;>t{JY`5e>tEv@3u3ZMWs(84{9Ik0O}Cx2&w~hjNvgP zWj=vAMN);SLDo=VIY6=ivk18eQ-`d>EI}@L+cRfdhFpeeKsI2SkWFtpL$U(13b_i? zf^2#1Ig&M)b;xy?4ag0cO~_4eyFju9vkkcovje%~wa=04!n7gVFnf@DUi-pn_aXOT z4j>O;4j~V{?WMCFK_0<$AUiPB+_ATHo$Unj1m+a-6efTSyzLt#&tT3V&tT3W&tWbg zFTCv)lIJilAYYRFE&AQpJeGGvngKl~8tT+$!R6ge(<$C0@j$1AeiOXw|MWU2v<}|i zTwUMxuY*rn9+)iGfzmdLlcH&Bl!hYhB7OUoOm>O5UwuxyO#Icju$j@hZOsV3h{0I2q}} zVNfb=82G}@fK)vX;X|$n zYX+V>bP(`F$SLE@3`ZC~>bxLS!!V}Hi7pdC3tb+k;9$X-1%vH%948oT!DH-JnR8*x zc^4PUa}r#KcX1Qs{k^!xR-U?#;NAs8F1DsR?o{kB*`4azEbcMcpXv@+JY;gjI^Q&5QZI-TX{k40Mu2 zsm{j57Z7}!@)(MFFx;F}oa+J4Nla03H4P#gCD~9+!4dPghhK}CvRBF+?Cq&{u#(K!t^?#-EFY>+8 zkG&_pN9QYzZ5VzxnmBZRFmwkK{Ui5ZydqMsHY$xuomQi|%Cxdu+o*KtZM((PzyAUj CxJ?8A literal 0 HcmV?d00001 diff --git a/magento2/api.pyc b/magento2/api.pyc index c000b18a58876e1092f7103bb16a0e8e76603fda..771567316f6f7c1183d45dccebfae7283864ba42 100644 GIT binary patch delta 1606 zcmdn$yD*T0`7T($Y5Fc%X|3^o*- z$w&)>H%X+>#9UyS65qU0QjR8uZ)TNdp^d@zGR!nFcyfiTE*A?h>pBB*vB~81vbsDe z%%Ei1%*Yt5!7^D-3Ohp!149%qLkb6&!N-uo z31;v!q;LTllP%cG7}++jXV2#$)3VKlB3w-5>OChmlaXA#yChP`)>|lLO19pg(kx`_ z4UlCfSMTIya=MdiWj9Rjme(XR$R@KZK#X{!ph2z?>54zd)Vg_-QXaVpWpc6VAu_F< Z+@WT~&dC9Wn-8iPk{$k_>>Q=x4gl=mZHfQ@ diff --git a/magento2/catalog.py b/magento2/catalog.py index c3beb57..2793d1f 100644 --- a/magento2/catalog.py +++ b/magento2/catalog.py @@ -10,7 +10,7 @@ import warnings from .api import API - +import logging class Category(API): """ @@ -28,7 +28,7 @@ def currentStore(self, store_view=None): args = [store_view] if store_view else {} return int(self.call('catalog_category.currentStore', args)) - def tree(self, parent_id=None, store_view=None): + def tree(self, rootCatId=2, store_view=1): """ Retrieve hierarchical tree of categories. @@ -36,7 +36,7 @@ def tree(self, parent_id=None, store_view=None): :param store_view: Store View (optional) :return: dictionary of values """ - return self.call('catalog_category.tree', [parent_id, store_view]) + return self.get('categories?rootCategoryId=%s' % (rootCatId,), {}) def level(self, website=None, store_view=None, parent_category=None): """ @@ -80,7 +80,7 @@ def info(self, category_id, store_view=None, attributes=None): } ) - def create(self, parent_id, data, store_view=None): + def create(self, parent_id, name, level=4): """ Create new category and return its ID @@ -89,14 +89,17 @@ def create(self, parent_id, data, store_view=None): :param store_view: Store view ID or Code :return: Integer ID """ - return int(self.post( - 'categories', - { - 'parent_id': parent_id, - 'data': data, - 'storeId': store_view + + data = { + 'category' : { + 'parent_id': parent_id, + 'level': level, + 'name' : name, + 'is_active': True + } } - )) + + return self.post('categories', data) def update(self, category_id, data, store_view=None): """ @@ -281,10 +284,7 @@ def info(self, product, store_view=None): :return: `dict` of values """ return self.get( - 'products/%s' % (product,), - { - 'storeId': store_view - } + 'products/%s' % (product,) ) def create(self, type_id, attribute_set_id, sku, data): @@ -297,15 +297,19 @@ def create(self, type_id, attribute_set_id, sku, data): :param data: Dictionary of data :return: INT id of product created """ - return self.post( - 'products', - { + product = { 'product': { 'type_id': type_id, 'attribute_set_id': attribute_set_id, 'sku': sku, - }.update(data) - } + } + } + + product['product'].update(data) + + return self.post( + 'products',product + ) def update(self, sku, data): diff --git a/magento2/catalog.pyc b/magento2/catalog.pyc index 77348981afa6e576545d9ead8e37b89055cefb33..adff8c2034cbf54e644f3dd21a8f6207288d001a 100644 GIT binary patch literal 37621 zcmeHQZH!#kS-$(>-JSK?Ydg*+X-&Dco6Lr8cau!d7AT=&{3>ZWEqp1Hd_ z@$Afc?j1YZ#2~d6UjiZi6bV(J@(T$N59`@NP@Q#WkLcOypnBAuJ*H<52GvL0+2eY4 zCa6B@&d%xCLqYX1clL2TdpM{*;m&?Q&&~$b54y7_^z0FK)|L4mU?2V-Hq#;gw#plE zBW*5L%4xaY+?bY=ms`zhyOM@0=45#O@@lx=Y`MZ=635HoW}0p%%S%hu_;$5fNfz}< z&8?-4cC8vGOShj}e0Hf7C+SkXmZXcD=~n$zuA=37tr9m9Jnh1jiwk^i_{!?aOP8*^ zWa?|S;;VUXNhnhaPNw8oi&Tmpr0 z37A1K&=va!WgpZO$gF{Yhe(UH!$EvFSObkv2Dm}_BSCy5SUW0bX64MWAU+nXJrYy} zLTkr^_;|4PXb@eQe}pgfA#6G+CGSsC$%V=&im->+5i9s5AI0YFk)Sop?Z!yZ7z=7a zaCbCFaXtv%KnINkZ;u9F#{VP02_KLo*vRvUl`C<&^r<)vEA3Vbpb3+d^toM&?-ctQ+T4`2eeduy4PTQ@?w}SoTF>FX3VRj%KnAjZ11|nGseG3J&-=a8e)#BT6xLJ!^M~juU-sassdB+x$YS*-Td~7FMgzKbd$ouo0(1`iNbc7O=5} zSF6zh91Vfu(h_w152E^n1P-PuJjWlKB-DKZJ6Y;t_`N+AY>Z{-I|g2-U83}DHsY`j zLf7+j4R7p(cj6mKEsdA_=v?ZcVHff3Ez99gn0vztMkq(>>P4-(2a%-)j`3Z}TU=Q( z?ktCw1!xp4hGAu^aImnvAmXT*XXhFWOk&TzmLMRV#fgr_ z3;`9Syfpf0>=*`6m>7{^&$ja=1Q;L24%x98@OQ@?N(3K|IR?Pu$-iNv-9#45KLv{R zvlXL6NTa+JdkmNxlyf^ul~@YQ032I6$4;0wLl8-LOjn&f5F)HvB!p%C1XlNr-E?QA zRZzRDZ1RVA-4~LW%x$FwoVRM_*Vk)x?39E}C)Zwo=9)d@_Tzf>+Vi;=dum@#5XN*3 z9A|&GRho@zjr^^Y?rg``o{w)OM4@d6*s!QY2EJ3ckb&CA6 z_s8kJ@o8@;rvUtob{Z$k99K|Q!gMnZ*K2@P5+>VmrH1{gzJY2qo{e?EImr;1KzK4q z$+3i^u2y?O;vlYPUfBSG8jf$@5I{k$MNkOWB8UUl%~E4zUL6I zL*7vl`dz_Zau}OndJNc^l>ZKoD@$QXfe($CoV{W-V#q}Rh&TeAfFozbup^Rh@VQRR zy|#_FjaC2Xf`;o?qYCG^iax`l$iQR8!HziTEV3&!a>7u##)W3H9+w;Xq;6IbQ8A8= zV&kcO19yozwB1g<9Epyxq!cFrhD77GbonM4kdR*mg@SO1$&qgQQd{nzZ&oMvhUs@u zP`l{WCQKejI2e**@$84P)N8iz5Pf1dpST1LV@4;LIR${Lu=A$!I+YRf7KH}1C^;M6 z*(|3a2BfG3;7;M4TD>0Lh^6)(BBTop9KEPGuLiT}0)>bow?h>+7~p-vH(cw6fCUal zHgyO8THUuWQlV|cpNfbN;GhEo)Eq#2PyTh3(SE-e*JBK6rH5XyRCE`19nww*?dtI^ zN$q<&C10va?dBxkLHc{}y&IiE-9Lk$F*YaFm7x7fo}X36Ft9en-D5ooU&D3nI#t8y zxg+AsNmAQr#MN-yI0uv6$5#gW`b;fc;Il<^4pa*h^WfzoRUEu*dh;2LKgm&P+- z#=#iM8^K3jn34@j0|rl_^a$#9Ep@&4%WMxxM7`+&_~Ezl1ns2rLMulpGPm-+Mn|T! z6Pu198sZzf(0KA3JG5`4jc|8ZW(UjPu|1&47ACKDqcejiM}G>noG2uF>qC4?dOtXO zu|ZL?ZQa5R9V;FJ8pu}+8f=4p11Ag=rok483I0^|*|TW;2i&Dpu||H`kx#Ag3YE7GX{e0b%fsJ7 zDW!Q7Q_4D0hwH7Tr|spL)x&V5<+I!r>DA9O6tut!4aQy37*}EMIx%oVxmQq z8_{uY7#4{_ZccJ@ikl~~NofqYF%`c^$i(Q(^z8JpLo7_h2(`3uz_HbSQsYODmR|5*PC||o<-~;^e&u`&I=P_NpenyMY&D1WsU}7Wra=Y z5!TyyXv2Esxt;R6q%Yqc;EPDGPCbsSF(Mf}E(Zolj?BNu&y0G+?kWT+e-0yoP3YuW7sHrR0Z3iRX`waMlF+AYmbo#uQDUsyXu-_(tTF1e*8G8zl5ay<`QyO|)0CE>~>MA!*V z!CM&{+bBL+4~;iQoKh=SJ44(~J$0s$k7DYJB#+~@tWK4u99+E__bxlh1gz*?*S6!G zh84FY!fl`ZD|QW43xcgn^8{Q(juS0#LqJE1+$?d!TO)GT=o8p@ILh9@hUmvpH$f%7 zripPQH3}0VcP+cF;IjO&`$Zo7ICgUKU_#^pazF4tL;{jMBw+|H#(PNEfK-4;fOx>w zaoIs02+~Dxra&RVeiBPn0O5p0M;eV>)8Rs{w!+(TQ4^#F&`<6iwhNKX2Ds+0dK01<7ZPltH=HRRmsv@p!$~h!aSJ7$Tx2 z-di_NkmbNO3sA$F&pvTb>G*dWsx+Kn^f5!&oU~6lN!KvrG;u34bUexgTkMz46c+~d z>@yey>?IkDwK^nR5iZ973Zw7Lz(vJ|Q28Pc5=3#sh#qnjY(y^?i2;8+efL1{HrVHQ z@WdNX1rW;{2}Y<9puAiWrY4Ft>r&9z*xqHEjhG4R^$O{kQW9#XbFo!xY=n%v?*d1K zRw;>X?vn(3^gJ>WeNviZG&kFLzJWP0V9BSgw#7_vD9>GG069P@I#w@T4Qo*Pb<~6= zDCSia;XiyA=mo}$=qwxCaEbgRH`@j)jIkO&SctvogFIyK8VmZ9+&jz7^W2=^<|%B3 zz_n=3o+P@cGyBlr;qjxRHYtXDOC@6w2ZT*FHHOF&B*7^HSW!|6ns{Tfl|_^FgjsSS z%MBT=5NSxycrc#Ui_i)nvG9zQ-3gHXif4ab_^>gyu3$+AKQq7*BaZG%ab092rUGyX z*bC8~PZE36*a^Vdn6v=Yv9bISKxA$A&%pd|lv3i_SX5crf?jd{@Vp0ocX|0$4gU`6D4Cl)FwD}Q)%W8i!NJ8{=;)U zc3|Kby9Nf1v1?%9$m9toy(9Y_WsvbM6DN@F4wHu+nsAT{;GN9Y7UdJu_cy?P-aVC~ zQ(Xj(kt?qio2QGH>*c-4cxT)9H4Ks`(HdKK#v}7FXbRNedi!XE0}_EEcs+^+1|R~O zt|}KC-anW7g*_61$zc77;LHSLp_x1irI2Tp_oo8g?A=u8(4n>4Qwg@Y(ZR}xKm+m< z8d&d~20yz;8d(3$Ze)010??L|klZaPcvW9QTu{5yR`Mw7%9tK8%;y9RupGIEA7at^P$Eiu=qpXb1$RH{B|KLgQc3AwMM^jQ&I3yE44fl}V)- z<$lN`J=yy#ZsXZXc01(#7^f#H-N|}|jE}09*=Wkj#0MhA49pC_xKm3vGbHWskCiGO zES#;$oM>%F*KIO*;ZfrlmO$g~#gRmSX`6gc*%v<7errGi)RNg77At za3+{pHhyWiI?A^(sk3~e9%p;TVbo_w(<`Fy;5e0bcJc_w+ykU0=IzFt^h-*Gp&GB3 z+x4{MUZ!L?7$f9*N?tHJk5m1b*pK0=Ffq|9+^y!$6TA~Yvl*CzM_0_d*)d@_{3p3ldBjwL=r}nyaq{iN8bCU zjhA_3Js<;S)|2RCcylAUUgX}8s2ig0-zIgZuoD!H7*_>O>=Js0TTFNqb+6>p*k~Vc z4zH)4gRcx?%nYS6!9Vp+_Ugqa@Vs8VNGE01i_IoV_YHnXhzN4r=Ns%i_C#>Nw1fhp zr~Oa;&m`MWRg7NZ=2P5ogqhr%SFq>bNA?Cb&R;;?*Z4BDCQMEYa$xv3TbM2!FU+Qo z;=*O)WIFFy;HNl&xq=Jw$9|v+)|~}&z&ZtNm~))IRh#@G{(GAJ6Pg9{G@fDMVk-=_ z#!#wMt3M1=R^9Dg>z^(01^@Jn)$d~N7JL~T(*V!t&yEN5H{?Hz3c4|D40*BUSyxXZ zhTt*N@JyV+gJEDeAWXlhpq`L)QSx_HSf9gz*JU`-syi%WW>|s5h{D(zR&o+`Ig7WmklCeu5FW7?`NArpS#y{^{$TTmP0%i{4+mig+1%q%)D1KOY|%*$W^V%n0S14p!38-~G>?!FU2{0BxoZF`vW zAhbxxNu78Mu{fn-k#5WP#bQ=~i4=Uu#3E;Sd9esFEkY4GEQF%Or<$#_)NEDbmUsw^ z-6|O)Ot}0ki&3QLBQR9WeO9+wtx7bwkJ8vvV!pV@C2WX2wR{ zl)(1<{%0koPVDBdZ-rIb!-? z<)IhZ4=1td1DLK@VTZOQ%G?z07d?TwKh*B&LburjO!fp_Bm@G@F8sY zQ%fZ8I+ujjF4y_Daf718y3W7XfHD4It@WK?#ymo1SnUBgvhY|xEaXDRd6kg8X;eZJ z#x$#uh+A_=@nqp>I{FN~K0+)|dksvsc>iQ^uPc`LW@T)=;vjniGo!Df?tdpIB;Qws z@$te@fx1=R#D85u0r^8|<2|9ao6{AMBo1Z5rBom%f`m&rsB@~Iafj&7Aj1OE z4K(Umwv-MO10)F!SO~3hQ9wa8E*n~3z`w~YY;qWy5)2{7M(3enAO9mi$E;3i4#(s$ zJNv+#N3M=pm(m;}V+V&w)WIQ=c4TQvm#cFERp)SN+NidPm?Z?IGV(8%LS&P}sRuj5* zi%GtBGF$sI%a7~i}z!0qTGb@^2S(idJAg?m+V2h z+1|R*Kvu#+2+!c+F1c78)}-pev?R3w8b0mMNuET5?{?db12ifdp`JLaEy&urGmRG78KLKidCB(D7_IFDW1kx+WKh z|7NA$t|Ce$Z|PS#W=!19T3im_3X5`MaXBnvrSnRwCTT`|`1WQqZ59{wt#}{*if*kL z-t%m6AuQUOc>GMS8>9ia`g&0s%v&;u<)LD>cKp)vKYBy7>lbzO9>eIRZpGhz z?6Oi`eU{~$-ecqTVUr$Rt4i@~d@P1{$sg`CZdiJG;%vxXzW;-8ly{+-`E9ap2fSY#m`R9@h#p%($QnYa!h68R zP>4SQUu=jIH?aR`OVQa_T@3VG>@ZK+ z-JatQYpfBHf1qRS!IoqEE}o-Gj?JBR8X5Zm@IXIg^T94PQ%w563SmBAnkgvB`iy?M zhnSU(w(I2zlJT(Q3$L-fR%$9>`P%@HEM7qu>!09 zk+b()$XZ1330|#YGkD)e6r$QcQ?~HLj1$2$)cT8H5xl(Bw^;TD=H=Z)-M`OfPhrPN z+H8RHm0IIwkKoi7P&_ADCnY$Ancy0n!an}j;FLMk;1muqiyU$nJ8pF>JBxgpS>%#c z^%80kOgl+jXKN^g!}(TRhY4FLYIM_sK zd=fjUap0Kns0+vOW=$)CqeCnh91lkZ zh&%FxLii9M3=r;;90_-<5C4B2MOA~)B}6)3i% z-Iq7^HF=fP*HNrB(qa#Hk~~`5D7tIrrYb%|iFB6Y-ArR-J+_{x^|Z`DSGWUx-4V6K zA>fzkJs(&69+DPZn&+C5{k}Zz%MzEO`HvHxEYD}aB^=8_rcRb6(IR_Yy_yz z(~_-O94=hK#eTlrdWR9<$FyfUW3c$kb`#LV=x@~VndxThCRdNzX|}`7GCnu+#oEm; zmTyW{Z84E*?2}6L+s3Wi^m2!<+7++1>+KCJqFiev_?BJWtvs|(j@5K8#G#RU8OQ8b zd#y&dM>mRNDs*w{_)e(eHS7EYwS0rmKC47^q0?XC#}f@MkHvw>19~1`)$ugB&)#kK z?cMWkU5P|ZckQ74xvF~iF((-B*2h=cSYh5bq9+R*PM4>gf`~;&I$HQS5`mmGV6Gev z_7_(CDsIpY&f(m5!h=%6NNkl0;;7Lhtz5Gj^Mit+=6(c2Qe?h2K3A@{-6ApUipYXTdynmrZNd06S!f8H!(e{r&(ok@4Zd+k0_{JnWJMqF4(0= z?U7@7t;>7d+^YE|U)m<_lUsSeHVvL$=8lrzEND^DrYj1#gy7Q4<$B`3oGOb4_QOJN z@06$xDD)9?2NC;T|XgUe;DG$1dZ?XbB<$o8YwEN6_ zxv)cnu4L4JApoGm;@y>uy{$=hOD1%`jl&_3j0)f#G{v%88>5p@Q%@z5GBUyhrXZp= z;gv|=blDpe8;Z8jii>PNCx}c;@bfiT+-bUSurMQGkJZL)%v*1^b}G0of2t~y3+Gft z^HDQ5A(3G^=!}^)o?S8w`%KV@oCEt+GUNE-w^J%N{XFX=oQJ-awh@H8)h^4*hBo#_ zUB~@aurFV&e%r0Q;s?U7xvHqLMQ#dX&ApVZke+|0GB`D^R_%_x*=yiUE?T#G5xS{- z&OOc?hM|*gJWR0pq=w`#06Ut_|N<9lM($b zZq^R5Qm0q%KNtrxksH5o90Q|5Q%IW1X1TEu=d&f^4Oua19`Tsx&zxDncUVfbMoGZ; zJaXl;b1|I8*yZ~=i8r%axxSEjM5zX1F7|fiqDQk#i`;w�^pHuaLM?*x`pT_yHeL z*V#TLF~y@9Q}BFYhTp|&$2k4cOu2-}`N`4X)**fd!+z2E@AxndH2$ro=vFMf)rP&b zgUq6BE=dKujowcAKs$^v%UZakmo?txdxTu|N}q#8J@HiwEsQTV2`b_I>~>ZT~`mP?daS4mnS-;nG&J$O_|hK3 literal 34539 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 diff --git a/magento2/checkout.pyc b/magento2/checkout.pyc index ae35349c508a081774cd559db6654fe221c03fd5..428b4c645c31ff6d88fe262423775413127ce388 100644 GIT binary patch delta 2350 zcmaD|e4vtp`7 zrXCCJT)xBLBW(+ppGH+Qi_?9^K{N}Q$^2TZn-k1#&?T*63+{)wzpDAM1{uFOP<{SsWIl-NJ{D|aWE)^EPZlSPr! z7xU**K ziSwr^zh$9B{~L`J6gXdO^IvUV3hWo%Tx7sXk^K*hK2VhKEllev2|fk0K#H7y%i<0N Z&KH|3ZyiX1^C#c5jwWBb=;S#zDgfUDKMDW< diff --git a/magento2/customer.pyc b/magento2/customer.pyc index 76b083e227d5478ce87ec1cbde145337c776754a..00842e6c261edcbf6daaafbdd223061bc41f90e3 100644 GIT binary patch delta 1382 zcmbQCb6TH+`7L67Uc#MDTuHQYwAf?^9w(YP{2b3iS~{F%^MAf#+F7tq;3CaJ=C+_O hO&q>CPMCux1_KkK)Z}dwJJCX#nEO=J7yzn$4jBLd delta 967 zcmX@DKSPIu`75Rk781yspb5r$G67%!}Dw8u3i*f^s^0QKtON#ZA z@=HrH3rkbei}Fhg@bYpK(^K+Pi%l+I%A|$aoB5gNG19_dprgen|6*~X Rnc1DJel#*$Y_c+&3;>-nu&@9C delta 329 zcmcb>yPt=H`7ik>7lkX(}U)44*^`b0@J&Z*FFFr-_>< YvB%QL$&=Ty`_aN^U=&JE_T`WU0AgO$bpQYW delta 393 zcmbOx^g@7x`7fii%E{L*wfQEi2L;wkP3~iNroj5ib{ziXYc~S` Df&q5S=9@%2Mq%X<(8o_96MI&`$2Q&)kr{XrXOi0grWFL@Hs!;TE8jxwx1T#Z=O>|83%c#x14hAi_ z&i=%2-$yX_^2`+ebOiy19^!W2qcI4q$OIhkH3En10q#aELI5`4Qv!@4N?S70p|mZ= z8l{mKUD5}G4&047G~XO#54ZaS&7q=0q8LUX2D+gb682!AFlT=dqV@c4T-#h09{ch+ zR{j=`y)-tPEMw<#*`7>vCZBkIjbC73oB2Ih=cc1a18&??G{afBFvBz{hL4xySz;HD zZ8-Vo*CDF!pQe0 zrR`5@>u1LHvlM&ICfPXgSy`OEaKB1yvgp^&*dke&`)@v|66by`ZF=s3HNLjR*??o} zcHD5sxx28k!h-{eGECA%Rty_j8MKoyg7S%LcMDIFs>)Yu8_r!>IL3ajgs;Nid$O#I zC?-vOBGPK~;hBt?Nm66o{-BGFW-ZE8b|Rum>qX@xpf!+(^G=>urSn=e%gfKpByGmM z`ORyVza)faL2L2Mvecqdj#?#4en)TcC`&{nD$Vuiv|05uLv4Ww-bF*5I}m9{Z3Lm} zg^}70M6f)O*Diwn0mi9dq0S3}Wf?-V5UZs39NiPSvcg-m#j0gdLb5HIL!bl|G93G7 zA`pH=RZcUB0+C|~ADYBMi960SYTwj;R@&^9C}qG93VE89J4PL z@d0;NheSRw#j)p+I9`-#opYDB_bADo(QB~uCO7;=Xi6mKhi3jX2RN&LVqMG0K_jjl z2poS>qhP(=-RyRntEo8r1s2uI`6@4`IAu)b82{a(Dy{$4TC|)i{nN3cOp<2gvOKqrD_!s_d?x6qx literal 3274 zcmds3OOG2x5U!rF#}9T<9%REK1Rq$*hXhLq?IDT~WB~~dd+1$>WG|z^)3(PM&kWr? zBwpF#u=#WR9)19PRpWgmz-^<1jMeSx>guYRufFb{@Xzi3$?tzW9@FM&MJ|0C(&v5TU&Za2!$E5>1EFwg_EHBN2L} z4~HH29CK)H9AqEOeTHtOXhjr52t>dz6hXos4i#(;h9P>-YvbDHl6dUPr^x(0*8OH| zHd)5bw7`AB-Q*j2j#=xPwV)2b0?RnX!W`1pwJ38z(+1i}M8!s>CLX zLG6q!l7)Hj>O_?|_pG$(r3q_%ZHo(q_jr=xx?A zMTCXYT^SWQSYA%Ef&p?j&{5|mc-T?fL8$s+r0zDHWJ69*IQay^IVVBDmpI8x11-Tv z*`#v}<%hZqW`ClX(YLwrCCxg8VE+EHwwg!9~TT7(yYZxnhEcW4s=j#DaFa&ND>c z*4TzNI}utm$a1ALznSdvscEVq(0f+fBy%PPg=2@J*G|6=<&a-hiXv{YY0KJyKIY33r139Z0VMQcmO^+W#I>VRThrV)PqC^;01F)jNSIAG4{+jW=%v zUGE~DhwlWiys2@5j%RNLuKZQ`-M`^F3nv146!o~R|(p6pB8m@^iBF9H)6l}G7AN0D-uerSVZjzI}gORHgOb mTZ^9gUH&pRV=rD_Q-%WI!v?ai`9sGsh@sk2*VMLZg?|GEka_t4 diff --git a/magento2/sales.pyc b/magento2/sales.pyc index 40114a35a50be78077405fcafcbd546132fbc0f7..e21a61798c3b6067b1981d9136443920748f04bb 100644 GIT binary patch delta 3067 zcmdl}{j-UYgZVQr*Y`(mQ5!k-F$X6yFfiz64`_Z(Jc8$Z;mj7rH7QWBo?LU=cnZ7>laj(WaQ`R z<|d}6=9T0dq4Os%)D@nr!=lK@w%L!Riir|a^w>XAqP>-a678GsaJDg0V*Ls3hm>gN z=e%^d>M$&U32O5&S4h4sh{_sv|QGHhhJ8Vw06k|~FveHiWKPfh@}1M@$7=n%`SvMe^Do5TH~4IfL^5`SG+;7hMZk`b ziP#rZhDb= zkF-0jgp!uKA@D&y2E`A1``l?RaH^v4(%BUkQs!#lBgk`(4YyiU$;3*8L>PBBKJudE zBU5zllAQquUkIen(V4keFk^(O!yD7GFjdyz2v38OHfE&=4ifD$GNQ_p*Hp~aP?|;9 zb!2lTVcM#ORYERv!`dGP1J)%U+b%$D5)*YxNB~El>05@Aa# z!E%5vtOXghO9yg)94!HexryozmYkUJoiSLUzmWwmp&@Q+r%$&m{WVV7>B+VwTw_u{Z@xI4du`3| zqaF=-813lHQB0vHU}7>j>Of%`N0M#-4=`6T^K6ps)GBcakEs+DX5mKQG4T6LQ>n8w k?a>MJXu&;-{N2$%-Z7PVS!y{d;1%(f?za}KM9XWx0Dm(oVE_OC diff --git a/magento2/version.pyc b/magento2/version.pyc index f8de333e29bf22d90d1aee3ecbd601a7eb058a5e..fcfd78ed282b13a78fc11b0574c1da4d73f4cd1b 100644 GIT binary patch delta 104 zcmdnYbb*P3`7Co?cG=x5~Trs}68=IIAiCTAoTe-(r{OU{O=f4*0RRAvCuINt delta 75 zcmcb>w3&&6`7lT{dX0LuLu5&!@I