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.
|
||||
"""))
|
||||
|
||||
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')
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue