Add customer, member importers from CORE DB
API is just not cutting it, need more flexibility
This commit is contained in:
parent
124a510c17
commit
d08c475223
|
@ -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)
|
|
@ -205,14 +205,20 @@ class CoreMember(model.Base):
|
||||||
Reference to the CORE-POS extension record for this member.
|
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.
|
``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):
|
def __str__(self):
|
||||||
return str(self.member)
|
return str(self.member)
|
||||||
|
|
||||||
CoreMember.make_proxy(model.Member, '_corepos', 'corepos_account_id')
|
CoreMember.make_proxy(model.Member, '_corepos', 'corepos_account_id')
|
||||||
|
CoreMember.make_proxy(model.Member, '_corepos', 'corepos_card_number')
|
||||||
|
|
||||||
|
|
||||||
class CoreMemberEquityPayment(model.Base):
|
class CoreMemberEquityPayment(model.Base):
|
||||||
|
|
|
@ -144,6 +144,8 @@ class CustomerImporter(FromCOREPOSAPI, corepos_importing.model.CustomerImporter)
|
||||||
'corepos_account_id',
|
'corepos_account_id',
|
||||||
'number',
|
'number',
|
||||||
'name',
|
'name',
|
||||||
|
# 'account_holder_first_name',
|
||||||
|
# 'account_holder_last_name',
|
||||||
'address_street',
|
'address_street',
|
||||||
'address_street2',
|
'address_street2',
|
||||||
'address_city',
|
'address_city',
|
||||||
|
@ -182,6 +184,8 @@ class CustomerImporter(FromCOREPOSAPI, corepos_importing.model.CustomerImporter)
|
||||||
'name': normalize_full_name(customer['firstName'],
|
'name': normalize_full_name(customer['firstName'],
|
||||||
customer['lastName']),
|
customer['lastName']),
|
||||||
|
|
||||||
|
# 'account_holder_first_name': customer['firstName'],
|
||||||
|
# 'account_holder_last_name': customer['lastName'],
|
||||||
'address_street': member['addressFirstLine'] or None,
|
'address_street': member['addressFirstLine'] or None,
|
||||||
'address_street2': member['addressSecondLine'] or None,
|
'address_street2': member['addressSecondLine'] or None,
|
||||||
'address_city': member['city'] 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.
|
Importer for member data from CORE POS API.
|
||||||
"""
|
"""
|
||||||
|
# TODO use this key instead
|
||||||
|
#key = 'corepos_card_number'
|
||||||
key = 'number'
|
key = 'number'
|
||||||
supported_fields = [
|
supported_fields = [
|
||||||
'number',
|
'number',
|
||||||
'corepos_account_id',
|
'corepos_account_id',
|
||||||
|
'corepos_card_number',
|
||||||
'customer_uuid',
|
'customer_uuid',
|
||||||
'person_first_name',
|
'person_first_name',
|
||||||
'person_last_name',
|
'person_last_name',
|
||||||
|
@ -857,6 +864,8 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
|
||||||
joined = datetime.datetime.strptime(member['startDate'],
|
joined = datetime.datetime.strptime(member['startDate'],
|
||||||
'%Y-%m-%d %H:%M:%S')
|
'%Y-%m-%d %H:%M:%S')
|
||||||
joined = joined.date()
|
joined = joined.date()
|
||||||
|
if joined == datetime.date(1900, 1, 1):
|
||||||
|
joined = None
|
||||||
|
|
||||||
withdrew = None
|
withdrew = None
|
||||||
if (member['endDate']
|
if (member['endDate']
|
||||||
|
@ -865,19 +874,20 @@ class MemberImporter(FromCOREPOSAPI, corepos_importing.model.MemberImporter):
|
||||||
withdrew = datetime.datetime.strptime(member['endDate'],
|
withdrew = datetime.datetime.strptime(member['endDate'],
|
||||||
'%Y-%m-%d %H:%M:%S')
|
'%Y-%m-%d %H:%M:%S')
|
||||||
withdrew = withdrew.date()
|
withdrew = withdrew.date()
|
||||||
|
if withdrew == datetime.date(1900, 1, 1):
|
||||||
|
withdrew = None
|
||||||
|
|
||||||
typeno = int(member['customerTypeID']) or None
|
typeno = int(member['customerTypeID'])
|
||||||
if typeno:
|
|
||||||
memtype = self.get_membership_type_by_number(typeno)
|
memtype = self.get_membership_type_by_number(typeno)
|
||||||
if not memtype:
|
if not memtype:
|
||||||
logger = log.warning if self.get_warn_for_unknown_membership_type() else log.debug
|
log.warning("unknown customerTypeID (membership_type_number) %s for: %s",
|
||||||
logger("unknown customerTypeID (membership_type_number) %s for: %s",
|
|
||||||
member['customerTypeID'], member)
|
member['customerTypeID'], member)
|
||||||
typeno = None
|
typeno = None
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'number': card_number,
|
'number': card_number,
|
||||||
'corepos_account_id': int(member['customerAccountID']),
|
'corepos_account_id': int(member['customerAccountID']),
|
||||||
|
'corepos_card_number': card_number,
|
||||||
'customer_uuid': customer.uuid if customer else None,
|
'customer_uuid': customer.uuid if customer else None,
|
||||||
'person_first_name': None,
|
'person_first_name': None,
|
||||||
'person_last_name': None,
|
'person_last_name': None,
|
||||||
|
|
|
@ -24,10 +24,12 @@
|
||||||
CORE POS (DB) -> Rattail data importing
|
CORE POS (DB) -> Rattail data importing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import logging
|
import logging
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
|
||||||
from corepos.db.office_op import model as corepos, Session as CoreSession
|
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):
|
def get_importers(self):
|
||||||
importers = OrderedDict()
|
importers = OrderedDict()
|
||||||
importers['Employee'] = EmployeeImporter
|
importers['Employee'] = EmployeeImporter
|
||||||
|
importers['Customer'] = CustomerImporter
|
||||||
|
importers['Member'] = MemberImporter
|
||||||
importers['Tax'] = TaxImporter
|
importers['Tax'] = TaxImporter
|
||||||
importers['Tender'] = TenderImporter
|
importers['Tender'] = TenderImporter
|
||||||
importers['Vendor'] = VendorImporter
|
importers['Vendor'] = VendorImporter
|
||||||
|
@ -90,6 +94,25 @@ class FromCOREPOS(importing.FromSQLAlchemy):
|
||||||
Base class for all CORE POS data importers.
|
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):
|
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):
|
class TaxImporter(FromCOREPOS, corepos_importing.model.TaxImporter):
|
||||||
"""
|
"""
|
||||||
Importer for tax data from CORE POS.
|
Importer for tax data from CORE POS.
|
||||||
|
|
|
@ -87,6 +87,7 @@ class MemberImporter(importing.model.MemberImporter):
|
||||||
extensions = {
|
extensions = {
|
||||||
'_corepos': [
|
'_corepos': [
|
||||||
'corepos_account_id',
|
'corepos_account_id',
|
||||||
|
'corepos_card_number',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue