diff --git a/rattail_corepos/corepos/commands.py b/rattail_corepos/corepos/commands.py new file mode 100644 index 0000000..998e10d --- /dev/null +++ b/rattail_corepos/corepos/commands.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# Rattail -- Retail Software Framework +# Copyright © 2010-2019 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 . +# +################################################################################ +""" +CORE-POS commands +""" + +from __future__ import unicode_literals, absolute_import + +import sys + +from rattail import commands +from rattail_corepos import __version__ +from rattail.util import load_object + + +def main(*args): + """ + Primary entry point for Crepes command system + """ + if args: + args = list(args) + else: + args = sys.argv[1:] + + cmd = Command() + cmd.run(*args) + + +class Command(commands.Command): + """ + Primary command for Crepes (CORE-POS) + """ + name = 'crepes' + version = __version__ + description = "Crepes -- command line interface for CORE-POS" + long_description = "" + + +class ImportToCore(commands.ImportSubcommand): + """ + Generic base class for commands which import *to* a CORE DB. + """ + # subclass must set these! + handler_key = None + default_handler_spec = None + + def get_handler_factory(self, **kwargs): + if self.config: + spec = self.config.get('rattail.corepos.importing', '{}.handler'.format(self.handler_key), + default=self.default_handler_spec) + else: + # just use default, for sake of cmd line help + spec = self.default_handler_spec + return load_object(spec) + + +class ExportCore(commands.ImportSubcommand): + """ + Export data to another CORE database + """ + name = 'export-core' + description = __doc__.strip() + default_handler_spec = 'rattail_corepos.corepos.importing.corepos:FromCoreToCoreExport' + default_dbkey = 'host' + + def get_handler_factory(self, **kwargs): + if self.config: + spec = self.config.get('rattail_corepos.exporting', 'corepos.handler', + default=self.default_handler_spec) + else: + # just use default, for sake of cmd line help + spec = self.default_handler_spec + return load_object(spec) + + def add_parser_args(self, parser): + super(ExportCore, self).add_parser_args(parser) + parser.add_argument('--dbkey', metavar='KEY', default=self.default_dbkey, + help="Config key for database engine to be used as the \"target\" " + "CORE DB, i.e. where data will be exported. This key must be " + "defined in the [rattail_corepos.db] section of your config file.") + + def get_handler_kwargs(self, **kwargs): + if 'args' in kwargs: + kwargs['dbkey'] = kwargs['args'].dbkey + return kwargs + + +class ImportCore(ImportToCore): + """ + Import data from another CORE database + """ + name = 'import-core' + description = __doc__.strip() + handler_key = 'corepos' + default_handler_spec = 'rattail_corepos.corepos.importing.corepos:FromCoreToCoreImport' + accepts_dbkey_param = True + + def add_parser_args(self, parser): + super(ImportCore, self).add_parser_args(parser) + if self.accepts_dbkey_param: + parser.add_argument('--dbkey', metavar='KEY', default='host', + help="Config key for database engine to be used as the CORE " + "\"host\", i.e. the source of the data to be imported. This key " + "must be defined in the [rattail_corepos.db] section of your config file. " + "Defaults to 'host'.") + + def get_handler_kwargs(self, **kwargs): + if self.accepts_dbkey_param: + if 'args' in kwargs: + kwargs['dbkey'] = kwargs['args'].dbkey + return kwargs diff --git a/rattail_corepos/corepos/importing/corepos.py b/rattail_corepos/corepos/importing/corepos.py new file mode 100644 index 0000000..d673da5 --- /dev/null +++ b/rattail_corepos/corepos/importing/corepos.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# Rattail -- Retail Software Framework +# Copyright © 2010-2019 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 . +# +################################################################################ +""" +CORE-POS -> CORE-POS data import +""" + +from __future__ import unicode_literals, absolute_import + +from rattail.importing.handlers import FromSQLAlchemyHandler, ToSQLAlchemyHandler +from rattail.importing.sqlalchemy import FromSQLAlchemySameToSame +from rattail.util import OrderedDict +from rattail_corepos.db import Session as CoreSession +from rattail_corepos.corepos import importing as corepos_importing + + +class FromCoreHandler(FromSQLAlchemyHandler): + """ + Base class for import handlers which use a CORE database as the host / source. + """ + host_title = "CORE" + + def make_host_session(self): + return CoreSession() + + +class ToCoreHandler(ToSQLAlchemyHandler): + """ + Base class for import handlers which target a CORE database on the local side. + """ + local_title = "CORE" + + def make_session(self): + return CoreSession() + + +class FromCoreToCoreBase(object): + """ + Common base class for Core -> Core data import/export handlers. + """ + + def get_importers(self): + importers = OrderedDict() + importers['Department'] = DepartmentImporter + importers['Subdepartment'] = SubdepartmentImporter + importers['Vendor'] = VendorImporter + importers['VendorContact'] = VendorContactImporter + importers['Product'] = ProductImporter + importers['ProductFlag'] = ProductFlagImporter + importers['Employee'] = EmployeeImporter + importers['Customer'] = CustomerImporter + importers['MemberType'] = MemberTypeImporter + importers['MemberInfo'] = MemberInfoImporter + importers['HouseCoupon'] = HouseCouponImporter + return importers + + +class FromCoreToCoreImport(FromCoreToCoreBase, FromCoreHandler, ToCoreHandler): + """ + Handler for CORE (other) -> CORE (local) data import. + + .. attribute:: direction + + Value is ``'import'`` - see also + :attr:`rattail.importing.handlers.ImportHandler.direction`. + """ + dbkey = 'host' + local_title = "CORE (default)" + + @property + def host_title(self): + return "CORE ({})".format(self.dbkey) + + def make_host_session(self): + return CoreSession(bind=self.config.corepos_engines[self.dbkey]) + + +class FromCoreToCoreExport(FromCoreToCoreBase, FromCoreHandler, ToCoreHandler): + """ + Handler for CORE (local) -> CORE (other) data export. + + .. attribute:: direction + + Value is ``'export'`` - see also + :attr:`rattail.importing.handlers.ImportHandler.direction`. + """ + direction = 'export' + host_title = "CORE (default)" + + @property + def local_title(self): + return "CORE ({})".format(self.dbkey) + + def make_session(self): + return CoreSession(bind=self.config.corepos_engines[self.dbkey]) + + +class FromCore(FromSQLAlchemySameToSame): + """ + Base class for CORE -> CORE data importers. + """ + + +class DepartmentImporter(FromCore, corepos_importing.model.DepartmentImporter): + pass + +class SubdepartmentImporter(FromCore, corepos_importing.model.SubdepartmentImporter): + pass + +class VendorImporter(FromCore, corepos_importing.model.VendorImporter): + pass + +class VendorContactImporter(FromCore, corepos_importing.model.VendorContactImporter): + pass + +class ProductImporter(FromCore, corepos_importing.model.ProductImporter): + pass + +class ProductFlagImporter(FromCore, corepos_importing.model.ProductFlagImporter): + pass + +class EmployeeImporter(FromCore, corepos_importing.model.EmployeeImporter): + pass + +class CustomerImporter(FromCore, corepos_importing.model.CustomerImporter): + pass + +class MemberTypeImporter(FromCore, corepos_importing.model.MemberTypeImporter): + pass + +class MemberInfoImporter(FromCore, corepos_importing.model.MemberInfoImporter): + pass + +class HouseCouponImporter(FromCore, corepos_importing.model.HouseCouponImporter): + pass diff --git a/rattail_corepos/corepos/importing/model.py b/rattail_corepos/corepos/importing/model.py index 08fad0f..a3a9fdb 100644 --- a/rattail_corepos/corepos/importing/model.py +++ b/rattail_corepos/corepos/importing/model.py @@ -12,19 +12,79 @@ from corepos.trans.db import model as coretrans class ToCore(importing.ToSQLAlchemy): - pass + """ + Base class for all CORE (operational) model importers + """ + # TODO: should we standardize on the 'id' primary key? (can we even?) + # key = 'id' class ToCoreTrans(importing.ToSQLAlchemy): pass -class CustomerImporter(ToCore): - """ - CORE-POS customer data importer. - """ - model_class = corepos.Customer +######################################## +# CORE Operational +######################################## +class DepartmentImporter(ToCore): + model_class = corepos.Department + key = 'number' + + +class SubdepartmentImporter(ToCore): + model_class = corepos.Subdepartment + key = 'number' + + +class VendorImporter(ToCore): + model_class = corepos.Vendor + key = 'id' + + +class VendorContactImporter(ToCore): + model_class = corepos.VendorContact + key = 'vendor_id' + + +class ProductImporter(ToCore): + model_class = corepos.Product + key = 'id' + + +class ProductFlagImporter(ToCore): + model_class = corepos.ProductFlag + key = 'bit_number' + + +class EmployeeImporter(ToCore): + model_class = corepos.Employee + key = 'emp_no' + + +class CustomerImporter(ToCore): + model_class = corepos.Customer + key = 'id' + + +class MemberTypeImporter(ToCore): + model_class = corepos.MemberType + key = 'id' + + +class MemberInfoImporter(ToCore): + model_class = corepos.MemberInfo + key = 'card_no' + + +class HouseCouponImporter(ToCore): + model_class = corepos.HouseCoupon + key = 'coupon_id' + + +######################################## +# CORE Transactions +######################################## class TransactionDetailImporter(ToCoreTrans): """ diff --git a/setup.py b/setup.py index 493b798..3cbc59a 100644 --- a/setup.py +++ b/setup.py @@ -99,6 +99,15 @@ setup( entry_points = { + 'console_scripts': [ + 'crepes = rattail_corepos.corepos.commands:main', + ], + + 'crepes.commands': [ + 'export-core = rattail_corepos.corepos.commands:ExportCore', + 'import-core = rattail_corepos.corepos.commands:ImportCore', + ], + 'rattail.config.extensions': [ 'rattail-corepos = rattail_corepos.config:RattailCOREPOSExtension', ],