Add proper importing for Customer/Person data from CORE API
includes datasync support. i think it even works right, but we'll see
This commit is contained in:
parent
4a409bb80a
commit
8f9f77b6b7
|
@ -46,6 +46,44 @@ class FromCOREAPIToRattail(NewDataSyncImportConsumer):
|
||||||
url = self.config.require('corepos.api', 'url')
|
url = self.config.require('corepos.api', 'url')
|
||||||
self.api = CoreWebAPI(url)
|
self.api = CoreWebAPI(url)
|
||||||
|
|
||||||
|
def process_changes(self, session, changes):
|
||||||
|
if self.runas_username:
|
||||||
|
session.set_continuum_user(self.runas_username)
|
||||||
|
|
||||||
|
# update all importers with current Rattail session
|
||||||
|
for importer in self.importers.values():
|
||||||
|
importer.session = session
|
||||||
|
# also establish the API client for each!
|
||||||
|
importer.establish_api()
|
||||||
|
|
||||||
|
# sync all Customer-related changes
|
||||||
|
types = [
|
||||||
|
'Customer',
|
||||||
|
]
|
||||||
|
for change in [c for c in changes if c.payload_type in types]:
|
||||||
|
if change.deletion:
|
||||||
|
# normal logic works fine for this (maybe?)
|
||||||
|
self.invoke_importer(session, change)
|
||||||
|
else:
|
||||||
|
# import customer data from API, into various Rattail tables
|
||||||
|
customer = self.get_host_object(session, change)
|
||||||
|
self.process_change(session, self.importers['Customer'],
|
||||||
|
host_object=customer)
|
||||||
|
people = self.importers['Person'].get_person_objects_for_customer(customer)
|
||||||
|
for person in people:
|
||||||
|
self.process_change(session, self.importers['Person'],
|
||||||
|
host_object=person)
|
||||||
|
|
||||||
|
# process all remaining supported models with typical logic
|
||||||
|
types = [
|
||||||
|
'Department',
|
||||||
|
'Subdepartment',
|
||||||
|
'Vendor',
|
||||||
|
'Product',
|
||||||
|
]
|
||||||
|
for change in [c for c in changes if c.payload_type in types]:
|
||||||
|
self.invoke_importer(session, change)
|
||||||
|
|
||||||
def get_host_object(self, session, change):
|
def get_host_object(self, session, change):
|
||||||
if change.payload_type == 'Customer':
|
if change.payload_type == 'Customer':
|
||||||
return self.api.get_customer(change.payload_key)
|
return self.api.get_customer(change.payload_key)
|
||||||
|
|
|
@ -27,10 +27,14 @@ CORE POS (API) -> Rattail data importing
|
||||||
import decimal
|
import decimal
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from sqlalchemy import orm
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
from corepos.api import CoreWebAPI
|
from corepos.api import CoreWebAPI
|
||||||
from corepos.db.office_op import Session as CoreSession, model as corepos
|
from corepos.db.office_op import Session as CoreSession, model as corepos
|
||||||
|
|
||||||
from rattail import importing
|
from rattail import importing
|
||||||
|
from rattail.db import model
|
||||||
from rattail.gpc import GPC
|
from rattail.gpc import GPC
|
||||||
from rattail.util import OrderedDict
|
from rattail.util import OrderedDict
|
||||||
from rattail.db.util import normalize_full_name, short_session
|
from rattail.db.util import normalize_full_name, short_session
|
||||||
|
@ -49,6 +53,8 @@ class FromCOREPOSToRattail(importing.ToRattailHandler):
|
||||||
def get_importers(self):
|
def get_importers(self):
|
||||||
importers = OrderedDict()
|
importers = OrderedDict()
|
||||||
importers['Customer'] = CustomerImporter
|
importers['Customer'] = CustomerImporter
|
||||||
|
importers['Person'] = PersonImporter
|
||||||
|
importers['CustomerPerson'] = CustomerPersonImporter
|
||||||
importers['Department'] = DepartmentImporter
|
importers['Department'] = DepartmentImporter
|
||||||
importers['Subdepartment'] = SubdepartmentImporter
|
importers['Subdepartment'] = SubdepartmentImporter
|
||||||
importers['Vendor'] = VendorImporter
|
importers['Vendor'] = VendorImporter
|
||||||
|
@ -69,21 +75,7 @@ class FromCOREPOSAPI(importing.Importer):
|
||||||
url = self.config.require('corepos.api', 'url')
|
url = self.config.require('corepos.api', 'url')
|
||||||
self.api = CoreWebAPI(url)
|
self.api = CoreWebAPI(url)
|
||||||
|
|
||||||
|
def get_core_customers(self):
|
||||||
class CustomerImporter(FromCOREPOSAPI, importing.model.CustomerImporter):
|
|
||||||
"""
|
|
||||||
Importer for customer data from CORE POS API.
|
|
||||||
"""
|
|
||||||
key = 'number'
|
|
||||||
supported_fields = [
|
|
||||||
'id',
|
|
||||||
'number',
|
|
||||||
'name',
|
|
||||||
'first_name',
|
|
||||||
'last_name',
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_host_objects(self):
|
|
||||||
# TODO: ideally could do this, but API doesn't let us fetch "all"
|
# TODO: ideally could do this, but API doesn't let us fetch "all"
|
||||||
# return self.api.get_customers()
|
# return self.api.get_customers()
|
||||||
|
|
||||||
|
@ -107,9 +99,24 @@ class CustomerImporter(FromCOREPOSAPI, importing.model.CustomerImporter):
|
||||||
dbcust.card_number)
|
dbcust.card_number)
|
||||||
|
|
||||||
self.progress_loop(fetch, db_customers,
|
self.progress_loop(fetch, db_customers,
|
||||||
message="Fetching Customer data from CORE-POS API")
|
message="Fetching Customer data from CORE API")
|
||||||
return list(customers.values())
|
return list(customers.values())
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerImporter(FromCOREPOSAPI, importing.model.CustomerImporter):
|
||||||
|
"""
|
||||||
|
Importer for customer data from CORE POS API.
|
||||||
|
"""
|
||||||
|
key = 'number'
|
||||||
|
supported_fields = [
|
||||||
|
'id',
|
||||||
|
'number',
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_host_objects(self):
|
||||||
|
return self.get_core_customers()
|
||||||
|
|
||||||
def normalize_host_object(self, customer):
|
def normalize_host_object(self, customer):
|
||||||
|
|
||||||
# figure out the "account holder" person for the customer
|
# figure out the "account holder" person for the customer
|
||||||
|
@ -128,8 +135,184 @@ class CustomerImporter(FromCOREPOSAPI, importing.model.CustomerImporter):
|
||||||
'number': customer['cardNo'],
|
'number': customer['cardNo'],
|
||||||
'name': normalize_full_name(person['firstName'],
|
'name': normalize_full_name(person['firstName'],
|
||||||
person['lastName']),
|
person['lastName']),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PersonImporter(FromCOREPOSAPI, corepos_importing.model.PersonImporter):
|
||||||
|
"""
|
||||||
|
Importer for person data from CORE POS API.
|
||||||
|
"""
|
||||||
|
key = 'corepos_customer_id'
|
||||||
|
supported_fields = [
|
||||||
|
'corepos_customer_id',
|
||||||
|
'first_name',
|
||||||
|
'last_name',
|
||||||
|
'display_name',
|
||||||
|
'customer_uuid',
|
||||||
|
'customer_person_ordinal',
|
||||||
|
]
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
super(PersonImporter, self).setup()
|
||||||
|
model = self.config.get_model()
|
||||||
|
|
||||||
|
self.customers = self.cache_model(model.Customer, key='id')
|
||||||
|
|
||||||
|
def get_host_objects(self):
|
||||||
|
|
||||||
|
# first get all customer data from CORE API
|
||||||
|
customers = self.get_core_customers()
|
||||||
|
normalized = []
|
||||||
|
|
||||||
|
# then collect all the "person" records
|
||||||
|
def normalize(customer, i):
|
||||||
|
normalized.extend(self.get_person_objects_for_customer(customer))
|
||||||
|
|
||||||
|
self.progress_loop(normalize, customers,
|
||||||
|
message="Collecting Person data from CORE")
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
def get_person_objects_for_customer(self, customer):
|
||||||
|
"""
|
||||||
|
Return a list of Person data objects for the given Customer. This
|
||||||
|
logic is split out separately so that datasync can leverage it too.
|
||||||
|
"""
|
||||||
|
records = []
|
||||||
|
|
||||||
|
# make sure we put the account holder first in the list!
|
||||||
|
people = sorted(customer['customers'],
|
||||||
|
key=lambda cust: 1 if cust['accountHolder'] else 0,
|
||||||
|
reverse=True)
|
||||||
|
|
||||||
|
for i, person in enumerate(people, 1):
|
||||||
|
person = dict(person)
|
||||||
|
person['customer_person_ordinal'] = i
|
||||||
|
records.append(person)
|
||||||
|
|
||||||
|
return records
|
||||||
|
|
||||||
|
def get_customer(self, id):
|
||||||
|
if hasattr(self, 'customers'):
|
||||||
|
return self.customers.get(id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.session.query(model.Customer)\
|
||||||
|
.filter(model.Customer.id == id)\
|
||||||
|
.one()
|
||||||
|
except NoResultFound:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def normalize_host_object(self, person):
|
||||||
|
|
||||||
|
customer = self.get_customer(person['customerAccountID'])
|
||||||
|
if not customer:
|
||||||
|
log.warning("Rattail customer not found for customerAccountID: %s",
|
||||||
|
person['customerAccountID'])
|
||||||
|
return
|
||||||
|
|
||||||
|
return {
|
||||||
|
'corepos_customer_id': int(person['customerID']),
|
||||||
'first_name': person['firstName'],
|
'first_name': person['firstName'],
|
||||||
'last_name': person['lastName'],
|
'last_name': person['lastName'],
|
||||||
|
'display_name': normalize_full_name(person['firstName'],
|
||||||
|
person['lastName']),
|
||||||
|
'customer_uuid': customer.uuid,
|
||||||
|
'customer_person_ordinal': person['customer_person_ordinal'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CustomerPersonImporter(FromCOREPOSAPI, importing.model.CustomerPersonImporter):
|
||||||
|
"""
|
||||||
|
Importer for customer-person linkage data from CORE POS API.
|
||||||
|
|
||||||
|
Note that we don't use this one in datasync, it's just for nightly
|
||||||
|
double-check.
|
||||||
|
"""
|
||||||
|
key = ('customer_uuid', 'person_uuid')
|
||||||
|
supported_fields = [
|
||||||
|
'customer_uuid',
|
||||||
|
'person_uuid',
|
||||||
|
'ordinal',
|
||||||
|
]
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
super(CustomerPersonImporter, self).setup()
|
||||||
|
model = self.config.get_model()
|
||||||
|
|
||||||
|
self.customers = self.cache_model(model.Customer, key='id')
|
||||||
|
|
||||||
|
query = self.session.query(model.Person)\
|
||||||
|
.join(model.CorePerson)\
|
||||||
|
.filter(model.CorePerson.corepos_customer_id != None)
|
||||||
|
self.people = self.cache_model(model.Person, query=query,
|
||||||
|
key='corepos_customer_id',
|
||||||
|
query_options=[orm.joinedload(model.Person._corepos)])
|
||||||
|
|
||||||
|
def get_host_objects(self):
|
||||||
|
|
||||||
|
# first get all customer data from CORE API
|
||||||
|
customers = self.get_core_customers()
|
||||||
|
normalized = []
|
||||||
|
|
||||||
|
# then collect all customer/person combination records
|
||||||
|
def normalize(customer, i):
|
||||||
|
# make sure we put the account holder first in the list!
|
||||||
|
people = sorted(customer['customers'],
|
||||||
|
key=lambda cust: 1 if cust['accountHolder'] else 0,
|
||||||
|
reverse=True)
|
||||||
|
for i, person in enumerate(people, 1):
|
||||||
|
normalized.append({
|
||||||
|
'customer_account_id': customer['customerAccountID'],
|
||||||
|
'person_customer_id': person['customerID'],
|
||||||
|
'ordinal': i,
|
||||||
|
})
|
||||||
|
|
||||||
|
self.progress_loop(normalize, customers,
|
||||||
|
message="Collecting CustomerPerson data from CORE")
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
def get_customer(self, id):
|
||||||
|
if hasattr(self, 'customers'):
|
||||||
|
return self.customers.get(id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.session.query(model.Customer)\
|
||||||
|
.filter(model.Customer.id == id)\
|
||||||
|
.one()
|
||||||
|
except NoResultFound:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_person(self, corepos_customer_id):
|
||||||
|
if hasattr(self, 'people'):
|
||||||
|
return self.people.get(corepos_customer_id)
|
||||||
|
|
||||||
|
model = self.config.get_model()
|
||||||
|
try:
|
||||||
|
return self.session.query(model.Person)\
|
||||||
|
.join(model.CorePerson)\
|
||||||
|
.filter(model.CorePerson.corepos_customer_id == corepos_customer_id)\
|
||||||
|
.one()
|
||||||
|
except NoResultFound:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def normalize_host_object(self, cp):
|
||||||
|
|
||||||
|
customer = self.get_customer(cp['customer_account_id'])
|
||||||
|
if not customer:
|
||||||
|
log.warning("Rattail customer not found for customerAccountID: %s",
|
||||||
|
cp['customer_account_id'])
|
||||||
|
return
|
||||||
|
|
||||||
|
person = self.get_person(int(cp['person_customer_id']))
|
||||||
|
if not person:
|
||||||
|
log.warning("Rattail person not found for customerID: %s",
|
||||||
|
cp['person_customer_id'])
|
||||||
|
return
|
||||||
|
|
||||||
|
return {
|
||||||
|
'customer_uuid': customer.uuid,
|
||||||
|
'person_uuid': person.uuid,
|
||||||
|
'ordinal': cp['ordinal'],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,25 @@ from rattail import importing
|
||||||
# core importer overrides
|
# core importer overrides
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
|
class PersonImporter(importing.model.PersonImporter):
|
||||||
|
|
||||||
|
extension_attr = '_corepos'
|
||||||
|
extension_fields = [
|
||||||
|
'corepos_customer_id',
|
||||||
|
]
|
||||||
|
|
||||||
|
def cache_query(self):
|
||||||
|
query = super(PersonImporter, self).cache_query()
|
||||||
|
model = self.config.get_model()
|
||||||
|
|
||||||
|
# we want to ignore people with no CORE ID, if that's (part of) our key
|
||||||
|
if 'corepos_customer_id' in self.key:
|
||||||
|
query = query.join(model.CorePerson)\
|
||||||
|
.filter(model.CorePerson.corepos_customer_id != None)
|
||||||
|
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
class VendorImporter(importing.model.VendorImporter):
|
class VendorImporter(importing.model.VendorImporter):
|
||||||
|
|
||||||
extension_attr = '_corepos'
|
extension_attr = '_corepos'
|
||||||
|
|
Loading…
Reference in a new issue