Add customer, member importers from CORE DB

API is just not cutting it, need more flexibility
This commit is contained in:
Lance Edgar 2023-10-12 10:34:46 -05:00
parent 124a510c17
commit d08c475223
5 changed files with 276 additions and 9 deletions

View file

@ -0,0 +1,38 @@
# -*- coding: utf-8; -*-
"""add core_member.corepos_card_number
Revision ID: 15bf65f68c52
Revises: 673ff7088d35
Create Date: 2023-10-12 07:39:34.608105
"""
# revision identifiers, used by Alembic.
revision = '15bf65f68c52'
down_revision = '673ff7088d35'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
import rattail.db.types
def upgrade():
# corepos_member
op.alter_column('corepos_member', 'corepos_account_id',
existing_type=sa.INTEGER(),
nullable=True)
op.add_column('corepos_member', sa.Column('corepos_card_number', sa.Integer(), nullable=True))
op.add_column('corepos_member_version', sa.Column('corepos_card_number', sa.Integer(), autoincrement=False, nullable=True))
def downgrade():
# corepos_member
op.drop_column('corepos_member_version', 'corepos_card_number')
op.drop_column('corepos_member', 'corepos_card_number')
op.alter_column('corepos_member', 'corepos_account_id',
existing_type=sa.INTEGER(),
nullable=False)

View file

@ -205,14 +205,20 @@ class CoreMember(model.Base):
Reference to the CORE-POS extension record for this member.
"""))
corepos_account_id = sa.Column(sa.Integer(), nullable=False, doc="""
corepos_account_id = sa.Column(sa.Integer(), nullable=True, doc="""
``Customers.customerAccountID`` value for this member, within CORE-POS.
""")
corepos_card_number = sa.Column(sa.Integer(), nullable=True, doc="""
``meminfo.card_no`` / ``CustomerAccounts.cardNo`` value for this
member, within CORE-POS.
""")
def __str__(self):
return str(self.member)
CoreMember.make_proxy(model.Member, '_corepos', 'corepos_account_id')
CoreMember.make_proxy(model.Member, '_corepos', 'corepos_card_number')
class CoreMemberEquityPayment(model.Base):

View file

@ -144,6 +144,8 @@ class CustomerImporter(FromCOREPOSAPI, corepos_importing.model.CustomerImporter)
'corepos_account_id',
'number',
'name',
# 'account_holder_first_name',
# 'account_holder_last_name',
'address_street',
'address_street2',
'address_city',
@ -182,6 +184,8 @@ class CustomerImporter(FromCOREPOSAPI, corepos_importing.model.CustomerImporter)
'name': normalize_full_name(customer['firstName'],
customer['lastName']),
# 'account_holder_first_name': customer['firstName'],
# 'account_holder_last_name': customer['lastName'],
'address_street': member['addressFirstLine'] or None,
'address_street2': member['addressSecondLine'] or None,
'address_city': member['city'] or None,
@ -783,10 +787,13 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
"""
Importer for member data from CORE POS API.
"""
# TODO use this key instead
#key = 'corepos_card_number'
key = 'number'
supported_fields = [
'number',
'corepos_account_id',
'corepos_card_number',
'customer_uuid',
'person_first_name',
'person_last_name',
@ -857,6 +864,8 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
joined = datetime.datetime.strptime(member['startDate'],
'%Y-%m-%d %H:%M:%S')
joined = joined.date()
if joined == datetime.date(1900, 1, 1):
joined = None
withdrew = None
if (member['endDate']
@ -865,19 +874,20 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
withdrew = datetime.datetime.strptime(member['endDate'],
'%Y-%m-%d %H:%M:%S')
withdrew = withdrew.date()
if withdrew == datetime.date(1900, 1, 1):
withdrew = None
typeno = int(member['customerTypeID']) or None
if typeno:
memtype = self.get_membership_type_by_number(typeno)
if not memtype:
logger = log.warning if self.get_warn_for_unknown_membership_type() else log.debug
logger("unknown customerTypeID (membership_type_number) %s for: %s",
member['customerTypeID'], member)
typeno = None
typeno = int(member['customerTypeID'])
memtype = self.get_membership_type_by_number(typeno)
if not memtype:
log.warning("unknown customerTypeID (membership_type_number) %s for: %s",
member['customerTypeID'], member)
typeno = None
data = {
'number': card_number,
'corepos_account_id': int(member['customerAccountID']),
'corepos_card_number': card_number,
'customer_uuid': customer.uuid if customer else None,
'person_first_name': None,
'person_last_name': None,

View file

@ -24,10 +24,12 @@
CORE POS (DB) -> Rattail data importing
"""
import datetime
import decimal
import logging
from collections import OrderedDict
import sqlalchemy as sa
from sqlalchemy import orm
from corepos.db.office_op import model as corepos, Session as CoreSession
@ -67,6 +69,8 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
def get_importers(self):
importers = OrderedDict()
importers['Employee'] = EmployeeImporter
importers['Customer'] = CustomerImporter
importers['Member'] = MemberImporter
importers['Tax'] = TaxImporter
importers['Tender'] = TenderImporter
importers['Vendor'] = VendorImporter
@ -90,6 +94,25 @@ class FromCOREPOS(importing.FromSQLAlchemy):
Base class for all CORE POS data importers.
"""
def setup(self):
super().setup()
self.ignore_new_members = self.should_ignore_new_members()
def should_ignore_new_members(self):
if hasattr(self, 'ignore_new_members'):
return self.ignore_new_members
return self.config.getbool('rattail_corepos',
'importing_ignore_new_members',
default=False)
def is_new_member(self, member):
if member.customers:
customer = member.customers[0]
if customer.last_name == 'NEW MEMBER'and not customer.first_name:
return True
return False
class EmployeeImporter(FromCOREPOS, corepos_importing.model.EmployeeImporter):
"""
@ -115,6 +138,195 @@ class EmployeeImporter(FromCOREPOS, corepos_importing.model.EmployeeImporter):
}
class CustomerImporter(FromCOREPOS, corepos_importing.model.CustomerImporter):
"""
Importer for customer data from CORE POS.
"""
host_model_class = corepos.MemberInfo
key = 'corepos_card_number'
supported_fields = [
'corepos_card_number',
'number',
'name',
'account_holder_uuid',
'account_holder_first_name',
'account_holder_last_name',
'email_address',
'phone_number',
'address_street',
'address_street2',
'address_city',
'address_state',
'address_zipcode',
]
def setup(self):
super().setup()
model = self.model
query = self.session.query(model.Person)\
.outerjoin(model.Customer,
model.Customer.account_holder_uuid == model.Person.uuid)\
.outerjoin(model.CoreCustomer)\
.outerjoin(model.Member,
model.Member.person_uuid == model.Person.uuid)\
.outerjoin(model.CoreMember)\
.filter(sa.or_(
model.CoreCustomer.corepos_card_number != None,
model.CoreMember.corepos_card_number != None))\
.options(orm.joinedload(model.Person.customer_accounts)\
.joinedload(model.Customer._corepos))
def card_number(person, normal):
customer = self.app.get_customer(person)
if customer and customer.corepos_card_number:
return customer.corepos_card_number
member = self.app.get_member(person)
if member and member.corepos_card_number:
return member.corepos_card_number
self.people_by_card_number = self.cache_model(model.Person, query=query,
key=card_number)
def get_person(self, member):
if hasattr(self, 'people_by_card_number'):
return self.people_by_card_number.get(member.card_number)
model = self.model
try:
return self.session.query(model.Person)\
.join(model.Customer,
model.Customer.account_holder_uuid == model.Person.uuid)\
.join(model.CoreCustomer)\
.filter(model.CoreCustomer.corepos_card_number == member.card_number)\
.one()
except orm.exc.NoResultFound:
pass
try:
return self.session.query(model.Person)\
.join(model.Member,
model.Member.person_uuid == model.Person.uuid)\
.join(model.CoreMember)\
.filter(model.CoreMember.corepos_card_number == member.card_number)\
.one()
except orm.exc.NoResultFound:
pass
def normalize_host_object(self, member):
card_number = member.card_number
# maybe ignore NEW MEMBER accounts
if self.should_ignore_new_members():
if self.is_new_member(member):
return
contact = member
if member.customers:
contact = member.customers[0]
person = self.get_person(member)
street = (member.street or '').split('\n')
return {
'corepos_card_number': card_number,
'number': card_number,
'name': normalize_full_name(contact.first_name, contact.last_name),
'account_holder_uuid': person.uuid if person else None,
'account_holder_first_name': contact.first_name,
'account_holder_last_name': contact.last_name,
'email_address': (member.email or '').strip() or None,
'phone_number': self.app.format_phone_number((member.phone or '').strip() or None),
'address_street': street[0] or None,
'address_street2': (street[1] or None) if len(street) > 1 else None,
'address_city': member.city or None,
'address_state': member.state or None,
'address_zipcode': member.zip or None,
}
class MemberImporter(FromCOREPOS, corepos_importing.model.MemberImporter):
"""
Importer for member data from CORE POS.
"""
host_model_class = corepos.MemberInfo
# TODO use this key instead
#key = 'corepos_card_number'
key = 'number'
supported_fields = [
'number',
'corepos_card_number',
'customer_uuid',
'person_first_name',
'person_last_name',
'membership_type_number',
'joined',
'withdrew',
'active',
]
def setup(self):
super().setup()
model = self.model
self.customers_by_number = self.app.cache_model(self.session,
model.Customer,
key='number')
def get_customer_by_number(self, number):
if hasattr(self, 'customers_by_number'):
return self.customers_by_number.get(number)
model = self.model
try:
return self.session.query(model.Customer)\
.filter(model.Customer.number == number)\
.one()
except orm.exc.NoResultFound:
pass
def normalize_host_object(self, core_member):
# maybe ignore NEW MEMBER accounts
if self.should_ignore_new_members():
if self.is_new_member(core_member):
return
core_customer = core_member.customers[0] if core_member.customers else None
core_contact = core_customer or core_member
card_number = core_member.card_number
customer = self.get_customer_by_number(card_number)
typeno = None
if core_customer and core_customer.member_type:
typeno = core_customer.member_type.id
joined = None
withdrew = None
if core_member.dates:
dates = core_member.dates
joined = dates.start_date.date() if dates.start_date else None
withdrew = dates.end_date.date() if dates.end_date else None
if joined and joined == datetime.date(1900, 1, 1):
joined = None
if withdrew and withdrew == datetime.date(1900, 1, 1):
withdrew = None
return {
'number': card_number,
'corepos_card_number': card_number,
'customer_uuid': customer.uuid if customer else None,
'person_first_name': core_contact.first_name,
'person_last_name': core_contact.last_name,
'membership_type_number': typeno,
'joined': joined,
'withdrew': withdrew,
'active': not bool(withdrew),
}
class TaxImporter(FromCOREPOS, corepos_importing.model.TaxImporter):
"""
Importer for tax data from CORE POS.

View file

@ -87,6 +87,7 @@ class MemberImporter(importing.model.MemberImporter):
extensions = {
'_corepos': [
'corepos_account_id',
'corepos_card_number',
],
}