Import Store, ProductCost from CORE DB
and tweak API importer accordingly
This commit is contained in:
parent
67861522eb
commit
b6e21f52ee
|
@ -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)
|
|
@ -195,11 +195,24 @@ class CoreProductCost(model.Base):
|
||||||
Reference to the CORE-POS extension record for this product cost.
|
Reference to the CORE-POS extension record for this product cost.
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
corepos_id = sa.Column(sa.Integer(), nullable=False, doc="""
|
corepos_vendor_id = sa.Column(sa.Integer(), nullable=False, doc="""
|
||||||
``vendorItemID`` value for the corresponding record within CORE-POS.
|
``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):
|
def __str__(self):
|
||||||
return str(self.cost)
|
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')
|
CoreProductCost.make_proxy(model.ProductCost, '_corepos', 'corepos_id')
|
||||||
|
|
|
@ -646,9 +646,12 @@ class ProductCostImporter(FromCOREPOSAPI, corepos_importing.model.ProductCostImp
|
||||||
"""
|
"""
|
||||||
Importer for product cost data from CORE POS API.
|
Importer for product cost data from CORE POS API.
|
||||||
"""
|
"""
|
||||||
|
# TODO: should change key after live sites are updated
|
||||||
key = ('vendor_uuid', 'code')
|
key = ('vendor_uuid', 'code')
|
||||||
|
# key = ('corepos_vendor_id', 'corepos_sku')
|
||||||
supported_fields = [
|
supported_fields = [
|
||||||
'corepos_id',
|
'corepos_vendor_id',
|
||||||
|
'corepos_sku',
|
||||||
'product_uuid',
|
'product_uuid',
|
||||||
'vendor_uuid',
|
'vendor_uuid',
|
||||||
'code',
|
'code',
|
||||||
|
@ -752,7 +755,8 @@ class ProductCostImporter(FromCOREPOSAPI, corepos_importing.model.ProductCostImp
|
||||||
case_cost = unit_cost * case_size
|
case_cost = unit_cost * case_size
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'corepos_id': int(item['vendorItemID']),
|
'corepos_vendor_id': int(item['vendorID']),
|
||||||
|
'corepos_sku': item['sku'],
|
||||||
'product_uuid': product.uuid,
|
'product_uuid': product.uuid,
|
||||||
'vendor_uuid': vendor.uuid,
|
'vendor_uuid': vendor.uuid,
|
||||||
'code': (item['sku'] or '').strip() or None,
|
'code': (item['sku'] or '').strip() or None,
|
||||||
|
|
|
@ -68,6 +68,7 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
|
||||||
|
|
||||||
def get_importers(self):
|
def get_importers(self):
|
||||||
importers = OrderedDict()
|
importers = OrderedDict()
|
||||||
|
importers['Store'] = StoreImporter
|
||||||
importers['Employee'] = EmployeeImporter
|
importers['Employee'] = EmployeeImporter
|
||||||
importers['Customer'] = CustomerImporter
|
importers['Customer'] = CustomerImporter
|
||||||
importers['Member'] = MemberImporter
|
importers['Member'] = MemberImporter
|
||||||
|
@ -77,6 +78,7 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
|
||||||
importers['Department'] = DepartmentImporter
|
importers['Department'] = DepartmentImporter
|
||||||
importers['Subdepartment'] = SubdepartmentImporter
|
importers['Subdepartment'] = SubdepartmentImporter
|
||||||
importers['Product'] = ProductImporter
|
importers['Product'] = ProductImporter
|
||||||
|
importers['ProductCost'] = ProductCostImporter
|
||||||
importers['MemberEquityPayment'] = MemberEquityPaymentImporter
|
importers['MemberEquityPayment'] = MemberEquityPaymentImporter
|
||||||
return importers
|
return importers
|
||||||
|
|
||||||
|
@ -114,6 +116,26 @@ class FromCOREPOS(importing.FromSQLAlchemy):
|
||||||
return False
|
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):
|
class EmployeeImporter(FromCOREPOS, corepos_importing.model.EmployeeImporter):
|
||||||
"""
|
"""
|
||||||
Importer for employee data from CORE POS.
|
Importer for employee data from CORE POS.
|
||||||
|
@ -445,7 +467,7 @@ class VendorImporter(FromCOREPOS, corepos_importing.model.VendorImporter):
|
||||||
model = self.config.get_model()
|
model = self.config.get_model()
|
||||||
|
|
||||||
# first get default query
|
# 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
|
# maybe filter a bit, to ensure only "relevant" records are involved
|
||||||
if 'corepos_id' in self.key:
|
if 'corepos_id' in self.key:
|
||||||
|
@ -546,6 +568,7 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
|
||||||
'sale_price_ends',
|
'sale_price_ends',
|
||||||
'sale_price_current',
|
'sale_price_current',
|
||||||
'food_stampable',
|
'food_stampable',
|
||||||
|
'discountable',
|
||||||
# 'tax1',
|
# 'tax1',
|
||||||
'tax_code',
|
'tax_code',
|
||||||
'not_for_sale',
|
'not_for_sale',
|
||||||
|
@ -629,6 +652,8 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
|
||||||
'sale_price_ends': None,
|
'sale_price_ends': None,
|
||||||
'sale_price_current': False,
|
'sale_price_current': False,
|
||||||
|
|
||||||
|
'discountable': bool(product.line_item_discountable),
|
||||||
|
|
||||||
'not_for_sale': not product.in_use,
|
'not_for_sale': not product.in_use,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,6 +684,113 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
|
||||||
return data
|
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):
|
class MemberEquityPaymentImporter(FromCOREPOS, corepos_importing.model.MemberEquityPaymentImporter):
|
||||||
"""
|
"""
|
||||||
Imports equity payment data from CORE-POS
|
Imports equity payment data from CORE-POS
|
||||||
|
|
|
@ -43,7 +43,7 @@ class PersonImporter(importing.model.PersonImporter):
|
||||||
}
|
}
|
||||||
|
|
||||||
def cache_query(self):
|
def cache_query(self):
|
||||||
query = super(PersonImporter, self).cache_query()
|
query = super().cache_query()
|
||||||
model = self.config.get_model()
|
model = self.config.get_model()
|
||||||
|
|
||||||
# we want to ignore people with no CORE ID, if that's (part of) our key
|
# 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):
|
def setup(self):
|
||||||
super(ProductImporter, self).setup()
|
super().setup()
|
||||||
|
|
||||||
if self.fields_active(self.size_fields):
|
if self.fields_active(self.size_fields):
|
||||||
app = self.config.get_app()
|
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)
|
self.uoms = handler.get_uom_sil_codes(self.session, uppercase=True)
|
||||||
|
|
||||||
def cache_query(self):
|
def cache_query(self):
|
||||||
query = super(ProductImporter, self).cache_query()
|
query = super().cache_query()
|
||||||
model = self.config.get_model()
|
model = self.config.get_model()
|
||||||
|
|
||||||
# we want to ignore products with no CORE ID, if that's (part of) our key
|
# 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 = {
|
extensions = {
|
||||||
'_corepos': [
|
'_corepos': [
|
||||||
|
'corepos_vendor_id',
|
||||||
|
'corepos_sku',
|
||||||
'corepos_id',
|
'corepos_id',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
def cache_query(self):
|
def cache_query(self):
|
||||||
query = super(ProductCostImporter, self).cache_query()
|
query = super().cache_query()
|
||||||
model = self.config.get_model()
|
model = self.config.get_model()
|
||||||
|
|
||||||
# we want to ignore items with no CORE ID, if that's (part of) our key
|
# we want to ignore items with no CORE ID, if that's (part of) our key
|
||||||
|
|
Loading…
Reference in a new issue