From d3e2619944d0f9d58bc9d27fedbf96adc38244de Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 20 Aug 2020 20:00:13 -0500 Subject: [PATCH] Refactor the Rattail <-> CORE product importers this should allow for more seamless "dual authority" mode --- rattail_corepos/corepos/importing/rattail.py | 37 +++++++++++-- rattail_corepos/importing/corepos/api.py | 58 +++++++++++++++++--- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/rattail_corepos/corepos/importing/rattail.py b/rattail_corepos/corepos/importing/rattail.py index 822e701..a2dba7f 100644 --- a/rattail_corepos/corepos/importing/rattail.py +++ b/rattail_corepos/corepos/importing/rattail.py @@ -286,13 +286,18 @@ class ProductImporter(FromRattail, corepos_importing.model.ProductImporter): 'scale', ] - def query(self): - query = super(ProductImporter, self).query() - return query.filter(model.Product.item_id != None) - def normalize_host_object(self, product): + upc = product.item_id + if not upc and product.upc: + upc = str(product.upc)[:-1] + if not upc: + log.warning("skipping product %s with unknown upc: %s", + product.uuid, product) + return + return { - 'upc': product.item_id, + '_product': product, + 'upc': upc, 'brand': product.brand.name if product.brand else '', 'description': product.description or '', 'size': product.size or '', @@ -301,3 +306,25 @@ class ProductImporter(FromRattail, corepos_importing.model.ProductImporter): 'foodstamp': '1' if product.food_stampable else '0', 'scale': '1' if product.weighed else '0', } + + def create_object(self, key, data): + + # must be sure not to pass the original Product instance, or else the + # API call will try to serialize and submit it + product = data.pop('_product') + + corepos_product = super(ProductImporter, self).create_object(key, data) + if corepos_product: + + # update our Rattail Product with the CORE ID + product.corepos_id = int(corepos_product['id']) + return corepos_product + + def update_object(self, corepos_product, data, local_data=None): + + # must be sure not to pass the original Product instance, or else the + # API call will try to serialize and submit it + product = data.pop('_product', None) + + corepos_product = super(ProductImporter, self).update_object(corepos_product, data, local_data) + return corepos_product diff --git a/rattail_corepos/importing/corepos/api.py b/rattail_corepos/importing/corepos/api.py index 71de890..da2d2be 100644 --- a/rattail_corepos/importing/corepos/api.py +++ b/rattail_corepos/importing/corepos/api.py @@ -29,7 +29,6 @@ import decimal import logging from sqlalchemy import orm -from sqlalchemy.orm.exc import NoResultFound from corepos.api import CoreWebAPI @@ -37,6 +36,7 @@ from rattail import importing from rattail.gpc import GPC from rattail.util import OrderedDict from rattail.time import localtime, make_utc +from rattail.core import get_uuid from rattail.db.util import normalize_full_name from rattail_corepos import importing as corepos_importing from rattail_corepos.corepos.util import get_core_members @@ -223,7 +223,7 @@ class PersonImporter(FromCOREPOSAPI, corepos_importing.model.PersonImporter): return self.session.query(model.Customer)\ .filter(model.Customer.id == id)\ .one() - except NoResultFound: + except orm.exc.NoResultFound: pass def normalize_host_object(self, person): @@ -311,7 +311,7 @@ class CustomerPersonImporter(FromCOREPOSAPI, importing.model.CustomerPersonImpor .join(model.CoreCustomer)\ .filter(model.CoreCustomer.corepos_account_id == account_id)\ .one() - except NoResultFound: + except orm.exc.NoResultFound: pass def get_person(self, corepos_customer_id): @@ -324,7 +324,7 @@ class CustomerPersonImporter(FromCOREPOSAPI, importing.model.CustomerPersonImpor .join(model.CorePerson)\ .filter(model.CorePerson.corepos_customer_id == corepos_customer_id)\ .one() - except NoResultFound: + except orm.exc.NoResultFound: pass def normalize_host_object(self, cp): @@ -432,8 +432,9 @@ class ProductImporter(FromCOREPOSAPI, corepos_importing.model.ProductImporter): """ Importer for product data from CORE POS API. """ - key = 'corepos_id' + key = 'uuid' supported_fields = [ + 'uuid', 'corepos_id', 'item_id', 'upc', @@ -451,9 +452,51 @@ class ProductImporter(FromCOREPOSAPI, corepos_importing.model.ProductImporter): # 'tax2', ] + def setup(self): + super(ProductImporter, self).setup() + model = self.config.get_model() + + query = self.session.query(model.Product)\ + .join(model.CoreProduct)\ + .filter(model.CoreProduct.corepos_id != None)\ + .options(orm.joinedload(model.Product._corepos)) + self.core_existing = self.cache_model(model.Product, key='corepos_id', + query=query) + def get_host_objects(self): return self.api.get_products() + def identify_product(self, corepos_product): + model = self.config.get_model() + corepos_id = int(corepos_product['id']) + + if hasattr(self, 'core_existing'): + product = self.core_existing.get(corepos_id) + if product: + return product + + else: + try: + return self.session.query(model.Product)\ + .join(model.CoreProduct)\ + .filter(model.CoreProduct.corepos_id == corepos_id)\ + .one() + except orm.exc.NoResultFound: + pass + + # at this point we'll search by `Product.item_id` instead + return self.session.query(model.Product)\ + .outerjoin(model.CoreProduct)\ + .filter(model.CoreProduct.corepos_id == None)\ + .filter(model.Product.item_id == corepos_product['upc'])\ + .first() + + def identify_product_uuid(self, corepos_product): + product = self.identify_product(corepos_product) + if product: + return product.uuid + return get_uuid() + def normalize_host_object(self, product): try: upc = GPC(product['upc'], calc_check_digit='upc') @@ -472,10 +515,11 @@ class ProductImporter(FromCOREPOSAPI, corepos_importing.model.ProductImporter): subdepartment_number = int(product['subdept']) or None price = None - if product['normal_price'] is not None: + if product.get('normal_price') is not None: price = decimal.Decimal(product['normal_price']) return { + 'uuid': self.identify_product_uuid(product), 'corepos_id': int(product['id']), 'item_id': product['upc'], 'upc': upc, @@ -570,7 +614,7 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter): return self.session.query(model.Customer)\ .filter(model.Customer.number == number)\ .one() - except NoResultFound: + except orm.exc.NoResultFound: pass def normalize_host_object(self, member):