Initial commit
basic import from Wave API to cache tables for Customers, Invoices
This commit is contained in:
commit
00e2a9fcb2
20 changed files with 1101 additions and 0 deletions
27
rattail_wave/importing/__init__.py
Normal file
27
rattail_wave/importing/__init__.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
rattail-harvest importing
|
||||
"""
|
||||
|
||||
from . import model
|
47
rattail_wave/importing/model.py
Normal file
47
rattail_wave/importing/model.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
rattail-wave model importers
|
||||
"""
|
||||
|
||||
from rattail.importing.model import ToRattail
|
||||
from rattail_wave.db import model
|
||||
|
||||
|
||||
##############################
|
||||
# cache models
|
||||
##############################
|
||||
|
||||
class WaveCacheCustomerImporter(ToRattail):
|
||||
model_class = model.WaveCacheCustomer
|
||||
|
||||
class WaveCacheInvoiceImporter(ToRattail):
|
||||
model_class = model.WaveCacheInvoice
|
||||
|
||||
|
||||
##############################
|
||||
# integration models
|
||||
##############################
|
||||
|
||||
class WaveCustomerImporter(ToRattail):
|
||||
model_class = model.WaveCustomer
|
59
rattail_wave/importing/rattail.py
Normal file
59
rattail_wave/importing/rattail.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Rattail -> Rattail data import for Wave integration
|
||||
"""
|
||||
|
||||
from rattail.importing import rattail as base
|
||||
from rattail_wave import importing as rattail_wave_importing
|
||||
|
||||
|
||||
class FromRattailToRattailWaveMixin(object):
|
||||
"""
|
||||
Add default registration of custom importers
|
||||
"""
|
||||
|
||||
def add_wave_importers(self, importers):
|
||||
importers['WaveCacheCustomer'] = WaveCacheCustomerImporter
|
||||
importers['WaveCacheInvoice'] = WaveCacheInvoiceImporter
|
||||
importers['WaveCustomer'] = WaveCustomerImporter
|
||||
return importers
|
||||
|
||||
|
||||
##############################
|
||||
# cache models
|
||||
##############################
|
||||
|
||||
class WaveCacheCustomerImporter(base.FromRattail, rattail_wave_importing.model.WaveCacheCustomerImporter):
|
||||
pass
|
||||
|
||||
class WaveCacheInvoiceImporter(base.FromRattail, rattail_wave_importing.model.WaveCacheInvoiceImporter):
|
||||
pass
|
||||
|
||||
|
||||
##############################
|
||||
# integration models
|
||||
##############################
|
||||
|
||||
class WaveCustomerImporter(base.FromRattail, rattail_wave_importing.model.WaveCustomerImporter):
|
||||
pass
|
57
rattail_wave/importing/versions.py
Normal file
57
rattail_wave/importing/versions.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Rattail -> Rattail "versions" data import
|
||||
"""
|
||||
|
||||
from rattail.importing import versions as base
|
||||
|
||||
|
||||
class WaveVersionMixin(object):
|
||||
|
||||
def add_wave_importers(self, importers):
|
||||
importers['WaveCacheCustomer'] = WaveCacheCustomerImporter
|
||||
importers['WaveCacheInvoice'] = WaveCacheInvoiceImporter
|
||||
importers['WaveCustomer'] = WaveCustomerImporter
|
||||
return importers
|
||||
|
||||
|
||||
class WaveCacheCustomerImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
def host_model_class(self):
|
||||
return self.model.WaveCacheCustomer
|
||||
|
||||
|
||||
class WaveCacheInvoiceImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
def host_model_class(self):
|
||||
return self.model.WaveCacheInvoice
|
||||
|
||||
|
||||
class WaveCustomerImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
def host_model_class(self):
|
||||
return self.model.WaveCustomer
|
252
rattail_wave/importing/wave.py
Normal file
252
rattail_wave/importing/wave.py
Normal file
|
@ -0,0 +1,252 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Wave -> Rattail ("wave cache") data import
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import decimal
|
||||
|
||||
from gql import Client, gql
|
||||
from gql.transport.requests import RequestsHTTPTransport
|
||||
|
||||
from rattail import importing
|
||||
from rattail.util import OrderedDict
|
||||
from rattail_wave import importing as rattail_wave_importing
|
||||
|
||||
|
||||
class FromWaveToRattail(importing.ToRattailHandler):
|
||||
"""
|
||||
Import handler for data coming from the Wave API
|
||||
"""
|
||||
host_key = 'wave'
|
||||
host_title = "Wave (API)"
|
||||
generic_host_title = "Wave (API)"
|
||||
|
||||
def get_importers(self):
|
||||
importers = OrderedDict()
|
||||
importers['WaveCacheCustomer'] = WaveCacheCustomerImporter
|
||||
importers['WaveCacheInvoice'] = WaveCacheInvoiceImporter
|
||||
return importers
|
||||
|
||||
|
||||
class FromWave(importing.Importer):
|
||||
"""
|
||||
Base class for all Wave importers
|
||||
"""
|
||||
key = 'id'
|
||||
|
||||
@property
|
||||
def supported_fields(self):
|
||||
fields = list(super(FromWave, self).supported_fields)
|
||||
fields.remove('uuid')
|
||||
return fields
|
||||
|
||||
def setup(self):
|
||||
super(FromWave, self).setup()
|
||||
self.setup_wave_api()
|
||||
|
||||
def setup_wave_api(self):
|
||||
token = self.config.require('wave', 'api.full_access_token')
|
||||
|
||||
self.wave_transport = RequestsHTTPTransport(
|
||||
url="https://gql.waveapps.com/graphql/public",
|
||||
headers={'Authorization': 'Bearer {}'.format(token)},
|
||||
verify=True,
|
||||
retries=3,
|
||||
)
|
||||
|
||||
self.wave_client = Client(transport=self.wave_transport,
|
||||
fetch_schema_from_transport=True)
|
||||
|
||||
self.wave_business_id = self.config.require('wave', 'business.id')
|
||||
|
||||
def date_from_wave(self, value):
|
||||
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
|
||||
|
||||
def money_from_wave(self, value):
|
||||
return decimal.Decimal('{:0.2f}'.format(value['raw'] / 100.0))
|
||||
|
||||
def time_from_wave(self, value):
|
||||
# all wave times appear to come as UTC, so no conversion needed
|
||||
value = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||
return value
|
||||
|
||||
def normalize_host_object(self, obj):
|
||||
data = dict(obj)
|
||||
|
||||
if 'internal_id' in self.fields:
|
||||
data['internal_id'] = data.pop('internalId')
|
||||
|
||||
if 'created_at' in self.fields:
|
||||
data['created_at'] = self.time_from_wave(data.pop('createdAt'))
|
||||
|
||||
if 'modified_at' in self.fields:
|
||||
data['modified_at'] = self.time_from_wave(data.pop('modifiedAt'))
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class WaveCacheCustomerImporter(FromWave, rattail_wave_importing.model.WaveCacheCustomerImporter):
|
||||
"""
|
||||
Import customer data from Wave
|
||||
"""
|
||||
|
||||
def get_host_objects(self):
|
||||
customers = []
|
||||
page = 1
|
||||
while True:
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
query {
|
||||
business(id: "%s") {
|
||||
id
|
||||
customers(page: %u, pageSize: 20, sort: [NAME_ASC]) {
|
||||
pageInfo {
|
||||
currentPage
|
||||
totalPages
|
||||
totalCount
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
internalId
|
||||
name
|
||||
email
|
||||
isArchived
|
||||
createdAt
|
||||
modifiedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""" % (self.wave_business_id, page)
|
||||
)
|
||||
|
||||
result = self.wave_client.execute(query)
|
||||
data = result['business']['customers']
|
||||
customers.extend([edge['node'] for edge in data['edges']])
|
||||
|
||||
if page >= data['pageInfo']['totalPages']:
|
||||
break
|
||||
|
||||
page += 1
|
||||
|
||||
return customers
|
||||
|
||||
def normalize_host_object(self, customer):
|
||||
data = super(WaveCacheCustomerImporter, self).normalize_host_object(customer)
|
||||
|
||||
data['is_archived'] = data.pop('isArchived')
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class WaveCacheInvoiceImporter(FromWave, rattail_wave_importing.model.WaveCacheInvoiceImporter):
|
||||
"""
|
||||
Import invoice data from Wave
|
||||
"""
|
||||
|
||||
def get_host_objects(self):
|
||||
invoices = []
|
||||
page = 1
|
||||
while True:
|
||||
|
||||
query = gql(
|
||||
"""
|
||||
query {
|
||||
business(id: "%s") {
|
||||
id
|
||||
invoices(page: %u, pageSize: 20) {
|
||||
pageInfo {
|
||||
currentPage
|
||||
totalPages
|
||||
totalCount
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
internalId
|
||||
customer {
|
||||
id
|
||||
}
|
||||
status
|
||||
title
|
||||
subhead
|
||||
invoiceNumber
|
||||
invoiceDate
|
||||
dueDate
|
||||
amountDue {
|
||||
raw
|
||||
}
|
||||
amountPaid {
|
||||
raw
|
||||
}
|
||||
taxTotal {
|
||||
raw
|
||||
}
|
||||
total {
|
||||
raw
|
||||
}
|
||||
discountTotal {
|
||||
raw
|
||||
}
|
||||
createdAt
|
||||
modifiedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""" % (self.wave_business_id, page)
|
||||
)
|
||||
|
||||
result = self.wave_client.execute(query)
|
||||
data = result['business']['invoices']
|
||||
invoices.extend([edge['node'] for edge in data['edges']])
|
||||
|
||||
if page >= data['pageInfo']['totalPages']:
|
||||
break
|
||||
|
||||
page += 1
|
||||
|
||||
return invoices
|
||||
|
||||
def normalize_host_object(self, invoice):
|
||||
data = super(WaveCacheInvoiceImporter, self).normalize_host_object(invoice)
|
||||
|
||||
customer = data.pop('customer')
|
||||
data['customer_id'] = customer['id']
|
||||
|
||||
data['invoice_number'] = data.pop('invoiceNumber')
|
||||
data['invoice_date'] = self.date_from_wave(data.pop('invoiceDate'))
|
||||
data['due_date'] = self.date_from_wave(data.pop('dueDate'))
|
||||
data['amount_due'] = self.money_from_wave(data.pop('amountDue'))
|
||||
data['amount_paid'] = self.money_from_wave(data.pop('amountPaid'))
|
||||
data['tax_total'] = self.money_from_wave(data.pop('taxTotal'))
|
||||
data['total'] = self.money_from_wave(data.pop('total'))
|
||||
data['discount_total'] = self.money_from_wave(data.pop('discountTotal'))
|
||||
|
||||
return data
|
Loading…
Add table
Add a link
Reference in a new issue