Import Store, ProductCost from CORE DB

and tweak API importer accordingly
This commit is contained in:
Lance Edgar 2023-10-20 14:37:25 -05:00
parent 67861522eb
commit b6e21f52ee
5 changed files with 199 additions and 9 deletions

View file

@ -0,0 +1,39 @@
# -*- coding: utf-8; -*-
"""fix FK for CoreProductCost
Revision ID: b5a8734b1fe0
Revises: 15bf65f68c52
Create Date: 2023-10-20 11:20:09.231682
"""
# revision identifiers, used by Alembic.
revision = 'b5a8734b1fe0'
down_revision = '15bf65f68c52'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
import rattail.db.types
def upgrade():
# corepos_product_cost
op.alter_column('corepos_product_cost', 'corepos_id', existing_type=sa.INTEGER(), nullable=True)
op.add_column('corepos_product_cost', sa.Column('corepos_vendor_id', sa.Integer(), nullable=False))
op.add_column('corepos_product_cost', sa.Column('corepos_sku', sa.String(length=13), nullable=False))
op.add_column('corepos_product_cost_version', sa.Column('corepos_vendor_id', sa.Integer(), autoincrement=False, nullable=True))
op.add_column('corepos_product_cost_version', sa.Column('corepos_sku', sa.String(length=13), autoincrement=False, nullable=True))
def downgrade():
# corepos_product_cost
op.drop_column('corepos_product_cost_version', 'corepos_sku')
op.drop_column('corepos_product_cost_version', 'corepos_vendor_id')
op.drop_column('corepos_product_cost', 'corepos_sku')
op.drop_column('corepos_product_cost', 'corepos_vendor_id')
op.alter_column('corepos_product_cost', 'corepos_id', existing_type=sa.INTEGER(), nullable=False)

View file

@ -195,11 +195,24 @@ class CoreProductCost(model.Base):
Reference to the CORE-POS extension record for this product cost.
"""))
corepos_id = sa.Column(sa.Integer(), nullable=False, doc="""
``vendorItemID`` value for the corresponding record within CORE-POS.
corepos_vendor_id = sa.Column(sa.Integer(), nullable=False, doc="""
``vendorItems.vendorID`` value for the corresponding record within
CORE-POS.
""")
corepos_sku = sa.Column(sa.String(length=13), nullable=False, doc="""
``vendorItems.sku`` value for the corresponding record within
CORE-POS.
""")
corepos_id = sa.Column(sa.Integer(), nullable=True, doc="""
``vendorItems.vendorItemID`` value for the corresponding record
within CORE-POS.
""")
def __str__(self):
return str(self.cost)
CoreProductCost.make_proxy(model.ProductCost, '_corepos', 'corepos_vendor_id')
CoreProductCost.make_proxy(model.ProductCost, '_corepos', 'corepos_sku')
CoreProductCost.make_proxy(model.ProductCost, '_corepos', 'corepos_id')

View file

@ -646,9 +646,12 @@ class ProductCostImporter(FromCOREPOSAPI, corepos_importing.model.ProductCostImp
"""
Importer for product cost data from CORE POS API.
"""
# TODO: should change key after live sites are updated
key = ('vendor_uuid', 'code')
# key = ('corepos_vendor_id', 'corepos_sku')
supported_fields = [
'corepos_id',
'corepos_vendor_id',
'corepos_sku',
'product_uuid',
'vendor_uuid',
'code',
@ -752,7 +755,8 @@ class ProductCostImporter(FromCOREPOSAPI, corepos_importing.model.ProductCostImp
case_cost = unit_cost * case_size
return {
'corepos_id': int(item['vendorItemID']),
'corepos_vendor_id': int(item['vendorID']),
'corepos_sku': item['sku'],
'product_uuid': product.uuid,
'vendor_uuid': vendor.uuid,
'code': (item['sku'] or '').strip() or None,

View file

@ -68,6 +68,7 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
def get_importers(self):
importers = OrderedDict()
importers['Store'] = StoreImporter
importers['Employee'] = EmployeeImporter
importers['Customer'] = CustomerImporter
importers['Member'] = MemberImporter
@ -77,6 +78,7 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
importers['Department'] = DepartmentImporter
importers['Subdepartment'] = SubdepartmentImporter
importers['Product'] = ProductImporter
importers['ProductCost'] = ProductCostImporter
importers['MemberEquityPayment'] = MemberEquityPaymentImporter
return importers
@ -114,6 +116,26 @@ class FromCOREPOS(importing.FromSQLAlchemy):
return False
class StoreImporter(FromCOREPOS, corepos_importing.model.StoreImporter):
"""
Importer for store data from CORE POS.
"""
host_model_class = corepos.Store
key = 'corepos_id'
supported_fields = [
'corepos_id',
'id',
'name',
]
def normalize_host_object(self, store):
return {
'corepos_id': store.id,
'id': str(store.id),
'name': store.description,
}
class EmployeeImporter(FromCOREPOS, corepos_importing.model.EmployeeImporter):
"""
Importer for employee data from CORE POS.
@ -445,7 +467,7 @@ class VendorImporter(FromCOREPOS, corepos_importing.model.VendorImporter):
model = self.config.get_model()
# first get default query
query = super(VendorImporter, self).cache_query()
query = super().cache_query()
# maybe filter a bit, to ensure only "relevant" records are involved
if 'corepos_id' in self.key:
@ -546,6 +568,7 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
'sale_price_ends',
'sale_price_current',
'food_stampable',
'discountable',
# 'tax1',
'tax_code',
'not_for_sale',
@ -629,6 +652,8 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
'sale_price_ends': None,
'sale_price_current': False,
'discountable': bool(product.line_item_discountable),
'not_for_sale': not product.in_use,
}
@ -659,6 +684,113 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
return data
class ProductCostImporter(FromCOREPOS, corepos_importing.model.ProductCostImporter):
"""
Importer for product cost data from CORE POS API.
"""
host_model_class = corepos.VendorItem
key = ('corepos_vendor_id', 'corepos_sku')
supported_fields = [
'corepos_vendor_id',
'corepos_sku',
'product_uuid',
'vendor_uuid',
'code',
'case_size',
'case_cost',
'unit_cost',
'preferred',
]
def setup(self):
super().setup()
model = self.model
query = self.session.query(model.Vendor)\
.join(model.CoreVendor)\
.filter(model.CoreVendor.corepos_id != None)
self.vendors_by_corepos_id = self.cache_model(model.Vendor,
query=query,
key='corepos_id')
self.products_by_item_id = self.cache_model(model.Product, key='item_id')
def query(self):
query = super().query()
query = query.options(orm.joinedload(corepos.VendorItem.product))
return query
def get_vendor(self, item):
corepos_id = item.vendor_id
if hasattr(self, 'vendors_by_corepos_id'):
return self.vendors_by_corepos_id.get(corepos_id)
model = self.config.get_model()
try:
return self.session.query(model.Vendor)\
.join(model.CoreVendor)\
.filter(model.CoreVendor.corepos_id == corepos_id)\
.one()
except orm.exc.NoResultFound:
pass
def get_product(self, item):
item_id = item.upc
if hasattr(self, 'products_by_item_id'):
return self.products_by_item_id.get(item_id)
model = self.model
try:
return self.session.query(model.Product)\
.filter(model.Product.item_id == item_id)\
.one()
except orm.exc.NoResultFound:
pass
def normalize_host_object(self, item):
vendor = self.get_vendor(item)
if not vendor:
log.warning("CORE POS vendor not found for item: %s", item)
return
product = self.get_product(item)
if not product:
# just debug logging since this is a common scenario; the
# CORE table is for items "available from vendor" but not
# necssarily items carried by store
log.debug("product not found for CORE vendor item: %s", item)
return
core_product = item.product
preferred = False
if core_product and core_product.default_vendor_id == item.vendor_id:
preferred = True
case_size = decimal.Decimal(str(item.units))
unit_cost = item.cost
case_cost = None
if unit_cost is not None:
case_cost = unit_cost * case_size
return {
'corepos_vendor_id': item.vendor_id,
'corepos_sku': item.sku,
'product_uuid': product.uuid,
'vendor_uuid': vendor.uuid,
'code': item.sku,
'case_size': case_size,
'case_cost': case_cost,
'unit_cost': unit_cost,
'preferred': preferred,
}
class MemberEquityPaymentImporter(FromCOREPOS, corepos_importing.model.MemberEquityPaymentImporter):
"""
Imports equity payment data from CORE-POS

View file

@ -43,7 +43,7 @@ class PersonImporter(importing.model.PersonImporter):
}
def cache_query(self):
query = super(PersonImporter, self).cache_query()
query = super().cache_query()
model = self.config.get_model()
# we want to ignore people with no CORE ID, if that's (part of) our key
@ -168,7 +168,7 @@ class ProductImporter(importing.model.ProductImporter):
}
def setup(self):
super(ProductImporter, self).setup()
super().setup()
if self.fields_active(self.size_fields):
app = self.config.get_app()
@ -176,7 +176,7 @@ class ProductImporter(importing.model.ProductImporter):
self.uoms = handler.get_uom_sil_codes(self.session, uppercase=True)
def cache_query(self):
query = super(ProductImporter, self).cache_query()
query = super().cache_query()
model = self.config.get_model()
# we want to ignore products with no CORE ID, if that's (part of) our key
@ -239,12 +239,14 @@ class ProductCostImporter(importing.model.ProductCostImporter):
extensions = {
'_corepos': [
'corepos_vendor_id',
'corepos_sku',
'corepos_id',
],
}
def cache_query(self):
query = super(ProductCostImporter, self).cache_query()
query = super().cache_query()
model = self.config.get_model()
# we want to ignore items with no CORE ID, if that's (part of) our key