Add support for importing MemberEquityPayment from CORE-POS DB
SQL only, no API for now
This commit is contained in:
parent
35e24422a2
commit
a57f29fe1a
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -55,7 +55,14 @@ class ImportCOREPOSDB(commands.ImportSubcommand):
|
|||
handler_key = 'to_rattail.from_corepos_db_office_op.import'
|
||||
|
||||
def add_parser_args(self, parser):
|
||||
super(ImportCOREPOSDB, self).add_parser_args(parser)
|
||||
super().add_parser_args(parser)
|
||||
|
||||
parser.add_argument('--corepos-dbtype', metavar='TYPE', default='office_op',
|
||||
choices=['office_op', 'office_trans'],
|
||||
help="Config *type* for CORE-POS database engine to be used as "
|
||||
"host. Default type is 'office_op' - this determines which "
|
||||
"config section is used with regard to the --corepos-dbkey arg.")
|
||||
|
||||
parser.add_argument('--corepos-dbkey', metavar='KEY', default='default',
|
||||
help="Config key for CORE POS database engine to be used as the \"host\", "
|
||||
"i.e. the source of the data to be imported. This key " "must be "
|
||||
|
@ -64,6 +71,7 @@ class ImportCOREPOSDB(commands.ImportSubcommand):
|
|||
|
||||
def get_handler_kwargs(self, **kwargs):
|
||||
if 'args' in kwargs:
|
||||
kwargs['corepos_dbtype'] = kwargs['args'].corepos_dbtype
|
||||
kwargs['corepos_dbkey'] = kwargs['args'].corepos_dbkey
|
||||
return kwargs
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""add MemberEquityPayment extension
|
||||
|
||||
Revision ID: 08d879bbe118
|
||||
Revises: b025df7cf41b
|
||||
Create Date: 2023-09-06 17:44:43.874500
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '08d879bbe118'
|
||||
down_revision = 'b025df7cf41b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import rattail.db.types
|
||||
|
||||
|
||||
|
||||
def upgrade():
|
||||
|
||||
# corepos_member_equity_payment
|
||||
op.create_table('corepos_member_equity_payment',
|
||||
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||
sa.Column('corepos_card_number', sa.Integer(), nullable=False),
|
||||
sa.Column('corepos_transaction_number', sa.String(length=50), nullable=True),
|
||||
sa.Column('corepos_transaction_id', sa.Integer(), nullable=True),
|
||||
sa.Column('corepos_department_number', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['uuid'], ['member_equity_payment.uuid'], name='corepos_member_equity_payment_fk_payment'),
|
||||
sa.PrimaryKeyConstraint('uuid')
|
||||
)
|
||||
op.create_table('corepos_member_equity_payment_version',
|
||||
sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False),
|
||||
sa.Column('corepos_card_number', sa.Integer(), autoincrement=False, nullable=True),
|
||||
sa.Column('corepos_transaction_number', sa.String(length=50), autoincrement=False, nullable=True),
|
||||
sa.Column('corepos_transaction_id', sa.Integer(), autoincrement=False, nullable=True),
|
||||
sa.Column('corepos_department_number', sa.Integer(), autoincrement=False, nullable=True),
|
||||
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
|
||||
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
|
||||
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('uuid', 'transaction_id')
|
||||
)
|
||||
op.create_index(op.f('ix_corepos_member_equity_payment_version_end_transaction_id'), 'corepos_member_equity_payment_version', ['end_transaction_id'], unique=False)
|
||||
op.create_index(op.f('ix_corepos_member_equity_payment_version_operation_type'), 'corepos_member_equity_payment_version', ['operation_type'], unique=False)
|
||||
op.create_index(op.f('ix_corepos_member_equity_payment_version_transaction_id'), 'corepos_member_equity_payment_version', ['transaction_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
|
||||
# corepos_member_equity_payment
|
||||
op.drop_index(op.f('ix_corepos_member_equity_payment_version_transaction_id'), table_name='corepos_member_equity_payment_version')
|
||||
op.drop_index(op.f('ix_corepos_member_equity_payment_version_operation_type'), table_name='corepos_member_equity_payment_version')
|
||||
op.drop_index(op.f('ix_corepos_member_equity_payment_version_end_transaction_id'), table_name='corepos_member_equity_payment_version')
|
||||
op.drop_table('corepos_member_equity_payment_version')
|
||||
op.drop_table('corepos_member_equity_payment')
|
|
@ -25,7 +25,8 @@ Database schema extensions for CORE-POS integration
|
|||
"""
|
||||
|
||||
from .stores import CoreStore
|
||||
from .people import CorePerson, CoreCustomer, CoreCustomerShopper, CoreMember
|
||||
from .people import (CorePerson, CoreCustomer, CoreCustomerShopper,
|
||||
CoreMember, CoreMemberEquityPayment)
|
||||
from .products import (CoreDepartment, CoreSubdepartment,
|
||||
CoreVendor, CoreProduct, CoreProductCost)
|
||||
|
||||
|
|
|
@ -178,3 +178,54 @@ class CoreMember(model.Base):
|
|||
return str(self.member)
|
||||
|
||||
CoreMember.make_proxy(model.Member, '_corepos', 'corepos_account_id')
|
||||
|
||||
|
||||
class CoreMemberEquityPayment(model.Base):
|
||||
"""
|
||||
CORE-specific extensions to
|
||||
:class:`~rattail:rattail.db.model.MemberEquityPayment`.
|
||||
"""
|
||||
__tablename__ = 'corepos_member_equity_payment'
|
||||
__table_args__ = (
|
||||
sa.ForeignKeyConstraint(['uuid'], ['member_equity_payment.uuid'],
|
||||
name='corepos_member_equity_payment_fk_payment'),
|
||||
)
|
||||
__versioned__ = {}
|
||||
|
||||
uuid = model.uuid_column(default=None)
|
||||
payment = orm.relationship(
|
||||
model.MemberEquityPayment,
|
||||
doc="""
|
||||
Reference to the actual payment record, which this one extends.
|
||||
""",
|
||||
backref=orm.backref(
|
||||
'_corepos',
|
||||
uselist=False,
|
||||
cascade='all, delete-orphan',
|
||||
doc="""
|
||||
Reference to the CORE-POS extension record for this payment.
|
||||
"""))
|
||||
|
||||
corepos_card_number = sa.Column(sa.Integer(), nullable=False, doc="""
|
||||
``stockpurchases.card_no`` value for this payment, within CORE-POS.
|
||||
""")
|
||||
|
||||
corepos_transaction_number = sa.Column(sa.String(length=50), nullable=True, doc="""
|
||||
``stockpurchases.trans_num`` value for this payment, within CORE-POS.
|
||||
""")
|
||||
|
||||
corepos_transaction_id = sa.Column(sa.Integer(), nullable=True, doc="""
|
||||
``stockpurchases.trans_id`` value for this payment, within CORE-POS.
|
||||
""")
|
||||
|
||||
corepos_department_number = sa.Column(sa.Integer(), nullable=True, doc="""
|
||||
``stockpurchases.dept`` value for this payment, within CORE-POS.
|
||||
""")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.payment)
|
||||
|
||||
CoreMemberEquityPayment.make_proxy(model.MemberEquityPayment, '_corepos', 'corepos_card_number')
|
||||
CoreMemberEquityPayment.make_proxy(model.MemberEquityPayment, '_corepos', 'corepos_transaction_number')
|
||||
CoreMemberEquityPayment.make_proxy(model.MemberEquityPayment, '_corepos', 'corepos_transaction_id')
|
||||
CoreMemberEquityPayment.make_proxy(model.MemberEquityPayment, '_corepos', 'corepos_department_number')
|
||||
|
|
|
@ -835,16 +835,7 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
|
|||
def normalize_host_object(self, member):
|
||||
card_number = member['cardNo']
|
||||
customer = self.get_customer_by_number(card_number)
|
||||
if not customer:
|
||||
log.warning("Rattail customer not found for cardNo %s: %s",
|
||||
card_number, member)
|
||||
return
|
||||
|
||||
person = self.app.get_person(customer)
|
||||
if not person:
|
||||
log.warning("Rattail person not found for cardNo %s: %s",
|
||||
card_number, member)
|
||||
return
|
||||
person = self.app.get_person(customer) if customer else None
|
||||
|
||||
# TODO: at first i was *skipping* non-member status records,
|
||||
# but since CORE sort of assumes all customers are members,
|
||||
|
@ -883,8 +874,8 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
|
|||
return {
|
||||
'number': card_number,
|
||||
'corepos_account_id': int(member['customerAccountID']),
|
||||
'customer_uuid': customer.uuid,
|
||||
'person_uuid': person.uuid,
|
||||
'customer_uuid': customer.uuid if customer else None,
|
||||
'person_uuid': person.uuid if person else None,
|
||||
'membership_type_number': typeno,
|
||||
'joined': joined,
|
||||
'withdrew': withdrew,
|
||||
|
|
|
@ -25,19 +25,27 @@ CORE POS (DB) -> Rattail data importing
|
|||
"""
|
||||
|
||||
import decimal
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from sqlalchemy import orm
|
||||
|
||||
from corepos.db.office_op import model as corepos, Session as CoreSession
|
||||
from corepos.db.office_trans import model as coretrans, Session as CoreTransSession
|
||||
|
||||
from rattail import importing
|
||||
from rattail.gpc import GPC
|
||||
from rattail_corepos import importing as corepos_importing
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailHandler):
|
||||
"""
|
||||
Import handler for data coming from a CORE POS database.
|
||||
"""
|
||||
# TODO: these should be changed, it now allows for "trans" DB too..
|
||||
generic_host_title = 'CORE Office (DB "op")'
|
||||
host_key = 'corepos_db_office_op'
|
||||
corepos_dbkey = 'default'
|
||||
|
@ -47,6 +55,12 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
|
|||
return "CORE-POS (DB/{})".format(self.corepos_dbkey)
|
||||
|
||||
def make_host_session(self):
|
||||
|
||||
# session type depends on the --corepos-dbtype arg
|
||||
if self.corepos_dbtype == 'office_trans':
|
||||
return CoreTransSession(bind=self.config.coretrans_engines[self.corepos_dbkey])
|
||||
|
||||
# assume office_op by default
|
||||
return CoreSession(bind=self.config.corepos_engines[self.corepos_dbkey])
|
||||
|
||||
def get_importers(self):
|
||||
|
@ -55,8 +69,17 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
|
|||
importers['Department'] = DepartmentImporter
|
||||
importers['Subdepartment'] = SubdepartmentImporter
|
||||
importers['Product'] = ProductImporter
|
||||
importers['MemberEquityPayment'] = MemberEquityPaymentImporter
|
||||
return importers
|
||||
|
||||
def get_default_keys(self):
|
||||
keys = super().get_default_keys()
|
||||
|
||||
if 'MemberEquityPayment' in keys:
|
||||
keys.remove('MemberEquityPayment')
|
||||
|
||||
return keys
|
||||
|
||||
|
||||
class FromCOREPOS(importing.FromSQLAlchemy):
|
||||
"""
|
||||
|
@ -228,3 +251,84 @@ class ProductImporter(FromCOREPOS, corepos_importing.model.ProductImporter):
|
|||
})
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class MemberEquityPaymentImporter(FromCOREPOS, corepos_importing.model.MemberEquityPaymentImporter):
|
||||
"""
|
||||
Imports equity payment data from CORE-POS
|
||||
"""
|
||||
host_model_class = coretrans.StockPurchase
|
||||
# TODO: this is composite key for StockPurchase, but may need to change?
|
||||
key = ('member_uuid', 'received', 'transaction_identifier')
|
||||
supported_fields = [
|
||||
'member_uuid',
|
||||
'amount',
|
||||
'received',
|
||||
'transaction_identifier',
|
||||
'corepos_card_number',
|
||||
'corepos_transaction_number',
|
||||
'corepos_transaction_id',
|
||||
'corepos_department_number',
|
||||
]
|
||||
|
||||
def setup(self):
|
||||
super().setup()
|
||||
model = self.model
|
||||
|
||||
query = self.session.query(model.Member)\
|
||||
.join(model.Customer)\
|
||||
.join(model.CoreCustomer)\
|
||||
.options(orm.joinedload(model.Member.customer)\
|
||||
.joinedload(model.Customer._corepos))
|
||||
key = lambda member, normal: member.customer.corepos_card_number
|
||||
self.members_by_card_number = self.cache_model(model.Member,
|
||||
query=query,
|
||||
key=key)
|
||||
|
||||
def get_member(self, card_number):
|
||||
if hasattr(self, 'members_by_card_number'):
|
||||
return self.members_by_card_number.get(card_number)
|
||||
|
||||
model = self.model
|
||||
try:
|
||||
return self.session.query(model.Member)\
|
||||
.join(model.Customer)\
|
||||
.join(model.CoreCustomer)\
|
||||
.filter(model.CoreCustomer.corepos_card_number == card_number)\
|
||||
.one()
|
||||
except orm.exc.NoResultFound:
|
||||
pass
|
||||
|
||||
def normalize_host_object(self, stock_purchase):
|
||||
|
||||
card_number = stock_purchase.card_number
|
||||
member = self.get_member(card_number)
|
||||
if not member:
|
||||
log.warning("member not found for card number %s: %s",
|
||||
card_number, stock_purchase)
|
||||
return
|
||||
|
||||
received = stock_purchase.datetime
|
||||
if received:
|
||||
received = self.app.localtime(received)
|
||||
received = self.app.make_utc(received)
|
||||
|
||||
return {
|
||||
'member_uuid': member.uuid,
|
||||
'amount': stock_purchase.amount,
|
||||
'received': received,
|
||||
'transaction_identifier': stock_purchase.transaction_number,
|
||||
'corepos_card_number': stock_purchase.card_number,
|
||||
'corepos_transaction_number': stock_purchase.transaction_number,
|
||||
'corepos_transaction_id': stock_purchase.transaction_id,
|
||||
'corepos_department_number': stock_purchase.department_number,
|
||||
}
|
||||
|
||||
def create_object(self, key, host_data):
|
||||
payment = super().create_object(key, host_data)
|
||||
if payment:
|
||||
|
||||
# track where each payment comes from!
|
||||
payment.source = 'corepos'
|
||||
|
||||
return payment
|
||||
|
|
|
@ -82,6 +82,18 @@ class MemberImporter(importing.model.MemberImporter):
|
|||
}
|
||||
|
||||
|
||||
class MemberEquityPaymentImporter(importing.model.MemberEquityPaymentImporter):
|
||||
|
||||
extensions = {
|
||||
'_corepos': [
|
||||
'corepos_card_number',
|
||||
'corepos_transaction_number',
|
||||
'corepos_transaction_id',
|
||||
'corepos_department_number',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class StoreImporter(importing.model.StoreImporter):
|
||||
|
||||
extensions = {
|
||||
|
|
|
@ -37,6 +37,7 @@ class CoreposVersionMixin(object):
|
|||
importers['CoreCustomer'] = CoreCustomerImporter
|
||||
importers['CoreCustomerShopper'] = CoreCustomerShopperImporter
|
||||
importers['CoreMember'] = CoreMemberImporter
|
||||
importers['CoreMemberEquityPayment'] = CoreMemberEquityPaymentImporter
|
||||
importers['CoreStore'] = CoreStoreImporter
|
||||
importers['CoreDepartment'] = CoreDepartmentImporter
|
||||
importers['CoreSubdepartment'] = CoreSubdepartmentImporter
|
||||
|
@ -76,6 +77,14 @@ class CoreMemberImporter(base.VersionImporter):
|
|||
return model.CoreMember
|
||||
|
||||
|
||||
class CoreMemberEquityPaymentImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
def host_model_class(self):
|
||||
model = self.config.get_model()
|
||||
return model.CoreMemberEquityPayment
|
||||
|
||||
|
||||
class CoreStoreImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
|
|
Loading…
Reference in a new issue