Add basic view support for CORE trans archive
This commit is contained in:
parent
148b4ccc1a
commit
149f5be052
|
@ -38,8 +38,14 @@ register(CoreOfficeSession)
|
||||||
CoreTransSession = scoped_session(sessionmaker())
|
CoreTransSession = scoped_session(sessionmaker())
|
||||||
register(CoreTransSession)
|
register(CoreTransSession)
|
||||||
|
|
||||||
# empty dict for now, this must populated on app startup
|
CoreTransArchiveSession = scoped_session(sessionmaker())
|
||||||
|
register(CoreTransArchiveSession)
|
||||||
|
|
||||||
|
# empty dict for now, this may be populated on app startup
|
||||||
ExtraCoreOfficeSessions = {}
|
ExtraCoreOfficeSessions = {}
|
||||||
|
|
||||||
# empty dict for now, this must populated on app startup
|
# empty dict for now, this may be populated on app startup
|
||||||
ExtraCoreTransSessions = {}
|
ExtraCoreTransSessions = {}
|
||||||
|
|
||||||
|
# empty dict for now, this may be populated on app startup
|
||||||
|
ExtraCoreTransArchiveSessions = {}
|
||||||
|
|
|
@ -39,12 +39,16 @@ class TailboneCorePosProvider(TailboneProvider):
|
||||||
def configure_db_sessions(self, rattail_config, pyramid_config):
|
def configure_db_sessions(self, rattail_config, pyramid_config):
|
||||||
from tailbone_corepos.db import (CoreOfficeSession,
|
from tailbone_corepos.db import (CoreOfficeSession,
|
||||||
CoreTransSession,
|
CoreTransSession,
|
||||||
|
CoreTransArchiveSession,
|
||||||
ExtraCoreOfficeSessions,
|
ExtraCoreOfficeSessions,
|
||||||
ExtraCoreTransSessions)
|
ExtraCoreTransSessions,
|
||||||
|
ExtraCoreTransArchiveSessions)
|
||||||
|
|
||||||
# CORE-POS DB(s)
|
# CORE-POS DB(s)
|
||||||
CoreOfficeSession.configure(bind=rattail_config.corepos_engine)
|
CoreOfficeSession.configure(bind=rattail_config.core_office_op_engine)
|
||||||
CoreTransSession.configure(bind=rattail_config.coretrans_engine)
|
CoreTransSession.configure(bind=rattail_config.core_office_trans_engine)
|
||||||
|
CoreTransArchiveSession.configure(
|
||||||
|
bind=rattail_config.core_office_trans_archive_engine)
|
||||||
|
|
||||||
# create session wrappers for each "extra" CORE DB engine
|
# create session wrappers for each "extra" CORE DB engine
|
||||||
for key, engine in rattail_config.core_office_op_engines.items():
|
for key, engine in rattail_config.core_office_op_engines.items():
|
||||||
|
@ -60,6 +64,13 @@ class TailboneCorePosProvider(TailboneProvider):
|
||||||
register(Session)
|
register(Session)
|
||||||
ExtraCoreTransSessions[key] = Session
|
ExtraCoreTransSessions[key] = Session
|
||||||
|
|
||||||
|
# and same for CORE Transaction Archive DB engine(s)
|
||||||
|
for key, engine in rattail_config.core_office_trans_archive_engines.items():
|
||||||
|
if key != 'default':
|
||||||
|
Session = scoped_session(sessionmaker(bind=engine))
|
||||||
|
register(Session)
|
||||||
|
ExtraCoreTransArchiveSessions[key] = Session
|
||||||
|
|
||||||
def get_provided_views(self):
|
def get_provided_views(self):
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%inherit file="/master/index.mako" />
|
||||||
|
|
||||||
|
<%def name="context_menu_items()">
|
||||||
|
${parent.context_menu_items()}
|
||||||
|
% if request.has_perm('corepos.transaction_details_archive'):
|
||||||
|
<li>${h.link_to("View Archive Details", url('corepos.transaction_details_archive'))}</li>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
|
@ -34,12 +34,11 @@ from tailbone.views import MasterView
|
||||||
from tailbone_corepos.db import CoreOfficeSession, ExtraCoreOfficeSessions
|
from tailbone_corepos.db import CoreOfficeSession, ExtraCoreOfficeSessions
|
||||||
|
|
||||||
|
|
||||||
class CoreOfficeMasterView(MasterView):
|
class CoreMasterView(MasterView):
|
||||||
"""
|
"""
|
||||||
Master base class for CORE-POS views
|
Master base class for all CORE-POS views
|
||||||
"""
|
"""
|
||||||
supports_multiple_engines = True
|
supports_multiple_engines = True
|
||||||
engine_type_key = 'corepos'
|
|
||||||
has_local_times = True
|
has_local_times = True
|
||||||
results_downloadable = True
|
results_downloadable = True
|
||||||
|
|
||||||
|
@ -47,41 +46,12 @@ class CoreOfficeMasterView(MasterView):
|
||||||
'id': "ID",
|
'id': "ID",
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_db_engines(self):
|
|
||||||
engines = OrderedDict()
|
|
||||||
if self.rattail_config.corepos_engine:
|
|
||||||
engines['default'] = self.rattail_config.corepos_engine
|
|
||||||
for dbkey in sorted(self.rattail_config.corepos_engines):
|
|
||||||
if dbkey != 'default':
|
|
||||||
engines[dbkey] = self.rattail_config.corepos_engines[dbkey]
|
|
||||||
return engines
|
|
||||||
|
|
||||||
@property
|
|
||||||
def Session(self):
|
|
||||||
"""
|
|
||||||
Which session we return will depend on user's "current" engine.
|
|
||||||
"""
|
|
||||||
dbkey = self.get_current_engine_dbkey()
|
|
||||||
|
|
||||||
if dbkey != 'default' and dbkey in ExtraCoreOfficeSessions:
|
|
||||||
return ExtraCoreOfficeSessions[dbkey]
|
|
||||||
|
|
||||||
return CoreOfficeSession
|
|
||||||
|
|
||||||
def make_isolated_session(self):
|
|
||||||
from corepos.db.office_op import Session as CoreSession
|
|
||||||
|
|
||||||
dbkey = self.get_current_engine_dbkey()
|
|
||||||
if dbkey != 'default' and dbkey in self.rattail_config.corepos_engines:
|
|
||||||
return CoreSession(bind=self.rattail_config.corepos_engines[dbkey])
|
|
||||||
|
|
||||||
return CoreSession()
|
|
||||||
|
|
||||||
def render_local_date(self, obj, field):
|
def render_local_date(self, obj, field):
|
||||||
value = getattr(obj, field)
|
value = getattr(obj, field)
|
||||||
if not value:
|
if not value:
|
||||||
return ""
|
return ""
|
||||||
value = localtime(self.rattail_config, value)
|
app = self.get_rattail_app()
|
||||||
|
value = app.localtime(value)
|
||||||
return str(value.date())
|
return str(value.date())
|
||||||
|
|
||||||
def render_corepos_store(self, obj, field):
|
def render_corepos_store(self, obj, field):
|
||||||
|
@ -142,3 +112,40 @@ class CoreOfficeMasterView(MasterView):
|
||||||
Subclass must define this logic; should return the "final" CORE Office
|
Subclass must define this logic; should return the "final" CORE Office
|
||||||
URL for the given object.
|
URL for the given object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CoreOfficeMasterView(CoreMasterView):
|
||||||
|
"""
|
||||||
|
Master base class for CORE Office views
|
||||||
|
"""
|
||||||
|
engine_type_key = 'corepos'
|
||||||
|
|
||||||
|
def get_db_engines(self):
|
||||||
|
engines = OrderedDict()
|
||||||
|
if self.rattail_config.core_office_engine:
|
||||||
|
engines['default'] = self.rattail_config.core_office_engine
|
||||||
|
for dbkey in self.rattail_config.core_office_engines:
|
||||||
|
if dbkey != 'default':
|
||||||
|
engines[dbkey] = self.rattail_config.core_office_engines[dbkey]
|
||||||
|
return engines
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Session(self):
|
||||||
|
"""
|
||||||
|
Which session we return will depend on user's "current" engine.
|
||||||
|
"""
|
||||||
|
dbkey = self.get_current_engine_dbkey()
|
||||||
|
|
||||||
|
if dbkey != 'default' and dbkey in ExtraCoreOfficeSessions:
|
||||||
|
return ExtraCoreOfficeSessions[dbkey]
|
||||||
|
|
||||||
|
return CoreOfficeSession
|
||||||
|
|
||||||
|
def make_isolated_session(self):
|
||||||
|
from corepos.db.office_op import Session as CoreSession
|
||||||
|
|
||||||
|
dbkey = self.get_current_engine_dbkey()
|
||||||
|
if dbkey != 'default' and dbkey in self.rattail_config.corepos_engines:
|
||||||
|
return CoreSession(bind=self.rattail_config.corepos_engines[dbkey])
|
||||||
|
|
||||||
|
return CoreSession()
|
||||||
|
|
30
tailbone_corepos/views/corepos/transactions/__init__.py
Normal file
30
tailbone_corepos/views/corepos/transactions/__init__.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- 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/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
CORE POS Transaction views
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
config.include('tailbone_corepos.views.corepos.transactions.current')
|
||||||
|
config.include('tailbone_corepos.views.corepos.transactions.archive')
|
83
tailbone_corepos/views/corepos/transactions/archive.py
Normal file
83
tailbone_corepos/views/corepos/transactions/archive.py
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# -*- 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/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
CORE-POS "archive" transaction views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from corepos.db.office_trans_archive import model as corearch
|
||||||
|
|
||||||
|
from rattail.util import OrderedDict
|
||||||
|
|
||||||
|
from .base import TransactionDetailMasterView
|
||||||
|
from tailbone_corepos.db import CoreTransArchiveSession, ExtraCoreTransArchiveSessions
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionDetailView(TransactionDetailMasterView):
|
||||||
|
"""
|
||||||
|
Master view for "archive" transaction details.
|
||||||
|
"""
|
||||||
|
model_class = corearch.TransactionDetail
|
||||||
|
model_title = "CORE-POS Archived Transaction Detail"
|
||||||
|
url_prefix = '/corepos/transaction-details/archive'
|
||||||
|
route_prefix = 'corepos.transaction_details_archive'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def Session(self):
|
||||||
|
"""
|
||||||
|
Which session we return will depend on user's "current" engine.
|
||||||
|
"""
|
||||||
|
dbkey = self.get_current_engine_dbkey()
|
||||||
|
|
||||||
|
if dbkey != 'default' and dbkey in ExtraCoreTransArchiveSessions:
|
||||||
|
return ExtraCoreTransArchiveSessions[dbkey]
|
||||||
|
|
||||||
|
return CoreTransArchiveSession
|
||||||
|
|
||||||
|
def get_db_engines(self):
|
||||||
|
engines = OrderedDict()
|
||||||
|
if self.rattail_config.core_office_trans_archive_engine:
|
||||||
|
engines['default'] = self.rattail_config.core_office_trans_archive_engine
|
||||||
|
for dbkey in self.rattail_config.core_office_trans_archive_engines:
|
||||||
|
if dbkey != 'default':
|
||||||
|
engines[dbkey] = self.rattail_config.core_office_trans_archive_engines[dbkey]
|
||||||
|
return engines
|
||||||
|
|
||||||
|
def make_isolated_session(self):
|
||||||
|
from corepos.db.office_trans import Session as CoreTransArchiveSession
|
||||||
|
|
||||||
|
dbkey = self.get_current_engine_dbkey()
|
||||||
|
if dbkey != 'default' and dbkey in self.rattail_config.core_office_trans_archive_engines:
|
||||||
|
return CoreTransArchiveSession(bind=self.rattail_config.core_office_trans_archive_engines[dbkey])
|
||||||
|
|
||||||
|
return CoreTransArchiveSession()
|
||||||
|
|
||||||
|
|
||||||
|
def defaults(config, **kwargs):
|
||||||
|
base = globals()
|
||||||
|
|
||||||
|
TransactionDetailView = kwargs.get('TransactionDetailView', base['TransactionDetailView'])
|
||||||
|
TransactionDetailView.defaults(config)
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
defaults(config)
|
80
tailbone_corepos/views/corepos/transactions/base.py
Normal file
80
tailbone_corepos/views/corepos/transactions/base.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
# -*- 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/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
CORE-POS transaction views (base)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from tailbone_corepos.views.corepos.master import CoreMasterView
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionDetailMasterView(CoreMasterView):
|
||||||
|
"""
|
||||||
|
Master view for "current" transaction details.
|
||||||
|
"""
|
||||||
|
labels = {
|
||||||
|
'store_row_id': "Store Row ID",
|
||||||
|
'store_id': "Store ID",
|
||||||
|
'pos_row_id': "POS Row ID",
|
||||||
|
'transaction_id': "Transaction ID",
|
||||||
|
'upc': "UPC",
|
||||||
|
}
|
||||||
|
|
||||||
|
grid_columns = [
|
||||||
|
'date_time',
|
||||||
|
'register_number',
|
||||||
|
'transaction_number',
|
||||||
|
'card_number',
|
||||||
|
'store_row_id',
|
||||||
|
'upc',
|
||||||
|
'description',
|
||||||
|
'quantity',
|
||||||
|
'unit_price',
|
||||||
|
'discount',
|
||||||
|
'total',
|
||||||
|
]
|
||||||
|
|
||||||
|
def configure_grid(self, g):
|
||||||
|
super(TransactionDetailMasterView, self).configure_grid(g)
|
||||||
|
|
||||||
|
g.set_type('quantity', 'quantity')
|
||||||
|
g.set_type('unit_price', 'currency')
|
||||||
|
g.set_type('discount', 'currency')
|
||||||
|
g.set_type('total', 'currency')
|
||||||
|
|
||||||
|
g.set_sort_defaults('date_time', 'desc')
|
||||||
|
|
||||||
|
g.set_label('register_number', "Register")
|
||||||
|
g.set_label('transaction_number', "Trans. No.")
|
||||||
|
g.set_label('card_number', "Card No.")
|
||||||
|
g.set_label('department_number', "Dept. No.")
|
||||||
|
|
||||||
|
g.set_link('upc')
|
||||||
|
g.set_link('description')
|
||||||
|
|
||||||
|
def configure_form(self, f):
|
||||||
|
super(TransactionDetailMasterView, self).configure_form(f)
|
||||||
|
|
||||||
|
f.set_type('quantity', 'quantity')
|
||||||
|
f.set_type('unit_price', 'currency')
|
||||||
|
f.set_type('discount', 'currency')
|
||||||
|
f.set_type('total', 'currency')
|
|
@ -26,46 +26,24 @@ CORE-POS transaction views
|
||||||
|
|
||||||
from corepos.db.office_trans import model as coretrans
|
from corepos.db.office_trans import model as coretrans
|
||||||
|
|
||||||
|
from rattail.util import OrderedDict
|
||||||
from rattail_corepos.corepos.importing.db.square import FromSquareToCoreTrans
|
from rattail_corepos.corepos.importing.db.square import FromSquareToCoreTrans
|
||||||
|
|
||||||
from .master import CoreOfficeMasterView
|
from .base import TransactionDetailMasterView
|
||||||
from tailbone_corepos.db import CoreTransSession, ExtraCoreTransSessions
|
from tailbone_corepos.db import CoreTransSession, ExtraCoreTransSessions
|
||||||
|
|
||||||
|
|
||||||
class TransactionDetailView(CoreOfficeMasterView):
|
class TransactionDetailView(TransactionDetailMasterView):
|
||||||
"""
|
"""
|
||||||
Master view for transaction details.
|
Master view for "current" transaction details.
|
||||||
"""
|
"""
|
||||||
model_class = coretrans.TransactionDetail
|
model_class = coretrans.TransactionDetail
|
||||||
model_title = "CORE-POS Transaction Detail"
|
model_title = "CORE-POS Current Transaction Detail"
|
||||||
url_prefix = '/corepos/transaction-details'
|
url_prefix = '/corepos/transaction-details/current'
|
||||||
route_prefix = 'corepos.transaction_details'
|
route_prefix = 'corepos.transaction_details'
|
||||||
deletable = True
|
|
||||||
bulk_deletable = True
|
bulk_deletable = True
|
||||||
supports_import_batch_from_file = True
|
supports_import_batch_from_file = True
|
||||||
|
|
||||||
labels = {
|
|
||||||
'store_row_id': "Store Row ID",
|
|
||||||
'store_id': "Store ID",
|
|
||||||
'pos_row_id': "POS Row ID",
|
|
||||||
'transaction_id': "Transaction ID",
|
|
||||||
'upc': "UPC",
|
|
||||||
}
|
|
||||||
|
|
||||||
grid_columns = [
|
|
||||||
'date_time',
|
|
||||||
'register_number',
|
|
||||||
'transaction_number',
|
|
||||||
'card_number',
|
|
||||||
'store_row_id',
|
|
||||||
'upc',
|
|
||||||
'description',
|
|
||||||
'quantity',
|
|
||||||
'unit_price',
|
|
||||||
'discount',
|
|
||||||
'total',
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def Session(self):
|
def Session(self):
|
||||||
"""
|
"""
|
||||||
|
@ -78,37 +56,24 @@ class TransactionDetailView(CoreOfficeMasterView):
|
||||||
|
|
||||||
return CoreTransSession
|
return CoreTransSession
|
||||||
|
|
||||||
def get_bulk_delete_session(self):
|
def get_db_engines(self):
|
||||||
from corepos.trans.db import Session as CoreTransSession
|
engines = OrderedDict()
|
||||||
|
if self.rattail_config.core_office_trans_engine:
|
||||||
|
engines['default'] = self.rattail_config.core_office_trans_engine
|
||||||
|
for dbkey in self.rattail_config.core_office_trans_engines:
|
||||||
|
if dbkey != 'default':
|
||||||
|
engines[dbkey] = self.rattail_config.core_office_trans_engines[dbkey]
|
||||||
|
return engines
|
||||||
|
|
||||||
|
def make_isolated_session(self):
|
||||||
|
from corepos.db.office_trans import Session as CoreTransSession
|
||||||
|
|
||||||
|
dbkey = self.get_current_engine_dbkey()
|
||||||
|
if dbkey != 'default' and dbkey in self.rattail_config.core_office_trans_engines:
|
||||||
|
return CoreTransSession(bind=self.rattail_config.core_office_trans_engines[dbkey])
|
||||||
|
|
||||||
return CoreTransSession()
|
return CoreTransSession()
|
||||||
|
|
||||||
def configure_grid(self, g):
|
|
||||||
super(TransactionDetailView, self).configure_grid(g)
|
|
||||||
|
|
||||||
g.set_type('quantity', 'quantity')
|
|
||||||
g.set_type('unit_price', 'currency')
|
|
||||||
g.set_type('discount', 'currency')
|
|
||||||
g.set_type('total', 'currency')
|
|
||||||
|
|
||||||
g.set_sort_defaults('date_time', 'desc')
|
|
||||||
|
|
||||||
g.set_label('register_number', "Register")
|
|
||||||
g.set_label('transaction_number', "Trans. No.")
|
|
||||||
g.set_label('card_number', "Card No.")
|
|
||||||
g.set_label('department_number', "Dept. No.")
|
|
||||||
|
|
||||||
g.set_link('upc')
|
|
||||||
g.set_link('description')
|
|
||||||
|
|
||||||
def configure_form(self, f):
|
|
||||||
super(TransactionDetailView, self).configure_form(f)
|
|
||||||
|
|
||||||
f.set_type('quantity', 'quantity')
|
|
||||||
f.set_type('unit_price', 'currency')
|
|
||||||
f.set_type('discount', 'currency')
|
|
||||||
f.set_type('total', 'currency')
|
|
||||||
|
|
||||||
def import_square(self):
|
def import_square(self):
|
||||||
return self.import_batch_from_file(FromSquareToCoreTrans, 'TransactionDetail',
|
return self.import_batch_from_file(FromSquareToCoreTrans, 'TransactionDetail',
|
||||||
importer_host_title="Square CSV")
|
importer_host_title="Square CSV")
|
Loading…
Reference in a new issue