diff --git a/rattail_corepos/config.py b/rattail_corepos/config.py
index 1e3e9e9..1d9994b 100644
--- a/rattail_corepos/config.py
+++ b/rattail_corepos/config.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2020 Lance Edgar
+# Copyright © 2010-2021 Lance Edgar
#
# This file is part of Rattail.
#
@@ -35,18 +35,33 @@ class RattailCOREPOSExtension(ConfigExtension):
key = 'rattail-corepos'
def configure(self, config):
- from corepos.db.office_op import Session as CoreSession
- from corepos.db.office_trans import Session as CoreTransSession
+ # office_op
+ from corepos.db.office_op import Session
engines = get_engines(config, section='corepos.db.office_op')
+ config.core_office_op_engines = engines
+ config.core_office_op_engine = engines.get('default')
+ Session.configure(bind=config.core_office_op_engine)
+ # TODO: deprecate / remove these next 2 lines
config.corepos_engines = engines
config.corepos_engine = engines.get('default')
- CoreSession.configure(bind=config.corepos_engine)
+ # office_trans
+ from corepos.db.office_trans import Session
engines = get_engines(config, section='corepos.db.office_trans')
+ config.core_office_trans_engines = engines
+ config.core_office_trans_engine = engines.get('default')
+ Session.configure(bind=config.core_office_trans_engine)
+ # TODO: deprecate / remove these next 2 lines
config.coretrans_engines = engines
config.coretrans_engine = engines.get('default')
- CoreTransSession.configure(bind=config.coretrans_engine)
+
+ # lane_op
+ from corepos.db.lane_op import Session
+ engines = get_engines(config, section='corepos.db.lane_op')
+ config.core_lane_op_engines = engines
+ config.core_lane_op_engine = engines.get('default')
+ Session.configure(bind=config.core_lane_op_engine)
def core_office_url(config, require=False, **kwargs):
diff --git a/rattail_corepos/corepos/common/__init__.py b/rattail_corepos/corepos/common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/rattail_corepos/corepos/common/importing.py b/rattail_corepos/corepos/common/importing.py
new file mode 100644
index 0000000..a8b3ee2
--- /dev/null
+++ b/rattail_corepos/corepos/common/importing.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2021 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 .
+#
+################################################################################
+"""
+Common importing logic for CORE-POS
+"""
+
+from rattail import importing
+
+
+class ToCore(importing.ToSQLAlchemy):
+ """
+ Base class for all CORE model importers; i.e. anything which uses
+ a CORE DB for the local side.
+ """
+
+ def create_object(self, key, host_data):
+
+ # NOTE! some tables in CORE DB may be using the MyISAM storage engine,
+ # which means it is *not* transaction-safe and therefore we cannot rely
+ # on "rollback" if in dry-run mode! in other words we better not touch
+ # the record at all, for dry run
+ if self.dry_run:
+ return host_data
+
+ return super(ToCore, self).create_object(key, host_data)
+
+ def update_object(self, obj, host_data, **kwargs):
+
+ # NOTE! some tables in CORE DB may be using the MyISAM storage engine,
+ # which means it is *not* transaction-safe and therefore we cannot rely
+ # on "rollback" if in dry-run mode! in other words we better not touch
+ # the record at all, for dry run
+ if self.dry_run:
+ return obj
+
+ return super(ToCore, self).update_object(obj, host_data, **kwargs)
+
+ def delete_object(self, obj):
+
+ # NOTE! some tables in CORE DB may be using the MyISAM storage engine,
+ # which means it is *not* transaction-safe and therefore we cannot rely
+ # on "rollback" if in dry-run mode! in other words we better not touch
+ # the record at all, for dry run
+ if self.dry_run:
+ return True
+
+ return super(ToCore, self).delete_object(obj)
diff --git a/rattail_corepos/corepos/lane/__init__.py b/rattail_corepos/corepos/lane/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/rattail_corepos/corepos/lane/importing/__init__.py b/rattail_corepos/corepos/lane/importing/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/rattail_corepos/corepos/lane/importing/op/__init__.py b/rattail_corepos/corepos/lane/importing/op/__init__.py
new file mode 100644
index 0000000..1dbd5fa
--- /dev/null
+++ b/rattail_corepos/corepos/lane/importing/op/__init__.py
@@ -0,0 +1,27 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2021 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 .
+#
+################################################################################
+"""
+Importing "operational" data into CORE Lane
+"""
+
+from . import model
diff --git a/rattail_corepos/corepos/lane/importing/op/model.py b/rattail_corepos/corepos/lane/importing/op/model.py
new file mode 100644
index 0000000..e9f4163
--- /dev/null
+++ b/rattail_corepos/corepos/lane/importing/op/model.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2021 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 .
+#
+################################################################################
+"""
+"operational" model importers for CORE Lane
+
+.. warning::
+ All classes in this module are "direct DB" importers, which will
+ write directly to MySQL. They are meant to be used in dry-run mode
+ only, and/or for sample data import to a dev system etc. They are
+ *NOT* meant for production use, as they will completely bypass any
+ CORE business rules logic which may exist.
+"""
+
+from rattail import importing
+from rattail_corepos.corepos.common.importing import ToCore
+
+from corepos.db.lane_op import model as corepos
+
+
+class ProductImporter(ToCore):
+ model_class = corepos.Product
+ key = 'id'
diff --git a/rattail_corepos/corepos/lane/importing/op/office.py b/rattail_corepos/corepos/lane/importing/op/office.py
new file mode 100644
index 0000000..43756f3
--- /dev/null
+++ b/rattail_corepos/corepos/lane/importing/op/office.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2021 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 Office -> CORE Lane import
+"""
+
+from corepos.db.office_op import Session as CoreOfficeSession, model as coreoffice
+from corepos.db.lane_op import Session as CoreLaneSession
+
+from rattail import importing
+from rattail.importing.handlers import FromSQLAlchemyHandler, ToSQLAlchemyHandler
+from rattail.util import OrderedDict
+from rattail_corepos.corepos.lane.importing import op as corepos_importing
+
+
+# TODO: this surely belongs in some other/common place? (is not lane-specific)
+class FromCoreOfficeHandler(FromSQLAlchemyHandler):
+ """
+ Base class for import handlers which use CORE Office as the host.
+ """
+ host_title = "CORE Office"
+
+ def make_host_session(self):
+ return CoreOfficeSession()
+
+
+# TODO: this surely belongs in some other/common place? (is not office-specific)
+class ToCoreLaneHandler(ToSQLAlchemyHandler):
+ """
+ Base class for import handlers which target CORE Lane on the local side.
+ """
+ local_title = "CORE Lane"
+
+ def make_session(self):
+ return CoreLaneSession()
+
+
+class FromCoreOfficeToCoreLane(FromCoreOfficeHandler, ToCoreLaneHandler):
+ """
+ Handler for CORE Office -> CORE Lane data export.
+ """
+ direction = 'export'
+
+ @property
+ def local_title(self):
+ return "CORE Lane ({})".format(self.dbkey)
+
+ def make_session(self):
+ return CoreLaneSession(bind=self.config.core_lane_op_engines[self.dbkey])
+
+ def get_importers(self):
+ importers = OrderedDict()
+ importers['Product'] = ProductImporter
+ return importers
+
+
+class FromCore(importing.FromSQLAlchemy):
+ """
+ Base class for CORE Office -> CORE Lane data importers.
+ """
+
+
+class ProductImporter(FromCore, corepos_importing.model.ProductImporter):
+ host_model_class = coreoffice.Product
+
+ # these fields are held in common, between Office and Lane tables
+ common_fields = [
+ 'id',
+ 'upc',
+ 'description',
+ 'brand',
+ 'formatted_name',
+ 'normal_price',
+ 'price_method',
+ 'group_price',
+ 'quantity',
+ 'special_price',
+ 'special_price_method',
+ 'special_group_price',
+ 'special_quantity',
+ # 'special_limit',
+ 'start_date',
+ 'end_date',
+ 'department_number',
+ 'size',
+ 'tax_rate_id',
+ 'foodstamp',
+ 'scale',
+ 'scale_price',
+ 'mix_match_code',
+ # 'created',
+ # 'modified',
+
+ # TODO: what to do about this 'replaces' thing?
+ # 'batchID'=>array('type'=>'TINYINT', 'replaces'=>'advertised'),
+ # batch_id = sa.Column('batchID', sa.SmallInteger(), nullable=True)
+ # advertised = sa.Column(sa.Boolean(), nullable=True)
+
+ 'tare_weight',
+ 'discount',
+ 'discount_type',
+ 'line_item_discountable',
+ 'unit_of_measure',
+ 'wicable',
+ 'quantity_enforced',
+ 'id_enforced',
+ 'cost',
+ # 'special_cost',
+ # 'received_cost',
+ 'in_use',
+ 'flags',
+ 'subdepartment_number',
+ 'deposit',
+ 'local',
+ 'store_id',
+ 'default_vendor_id',
+ 'current_origin_id',
+ # 'auto_par',
+ # 'price_rule_id',
+ 'last_sold',
+ ]
+
+ @property
+ def supported_fields(self):
+ return self.common_fields
+
+ def normalize_host_object(self, product):
+ data = dict([(field, getattr(product, field))
+ for field in self.common_fields])
+ return data
diff --git a/rattail_corepos/corepos/office/__init__.py b/rattail_corepos/corepos/office/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/rattail_corepos/corepos/office/commands.py b/rattail_corepos/corepos/office/commands.py
new file mode 100644
index 0000000..d4995a7
--- /dev/null
+++ b/rattail_corepos/corepos/office/commands.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2021 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 Office commands
+"""
+
+import sys
+
+from rattail import commands
+from rattail_corepos import __version__
+from rattail.util import load_object
+
+
+def main(*args):
+ """
+ Entry point for 'core-office' commands
+ """
+ if args:
+ args = list(args)
+ else:
+ args = sys.argv[1:]
+
+ cmd = Command()
+ cmd.run(*args)
+
+
+class Command(commands.Command):
+ """
+ Primary command for CORE Office
+ """
+ name = 'core-office'
+ version = __version__
+ description = "core-office -- command line interface for CORE Office"
+ long_description = ""
+
+
+class ExportLaneOp(commands.ImportSubcommand):
+ """
+ Export "op" data from CORE Office to CORE Lane
+ """
+ name = 'export-lane-op'
+ description = __doc__.strip()
+ default_handler_spec = 'rattail_corepos.corepos.lane.importing.op.office:FromCoreOfficeToCoreLane'
+ default_dbkey = 'default'
+
+ def get_handler_factory(self, **kwargs):
+ if self.config:
+ spec = self.config.get('corepos.lane.importing', 'office.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(ExportLaneOp, 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 Lane DB, i.e. where data will be "
+ " exported. This key must be defined in the "
+ " [rattail_corepos.db.lane_op] section of your "
+ "config file.")
+
+ def get_handler_kwargs(self, **kwargs):
+ if 'args' in kwargs:
+ kwargs['dbkey'] = kwargs['args'].dbkey
+ return kwargs
diff --git a/setup.py b/setup.py
index 5dcc8b4..2d32337 100644
--- a/setup.py
+++ b/setup.py
@@ -98,6 +98,11 @@ setup(
'console_scripts': [
'crepes = rattail_corepos.corepos.commands:main',
+ 'core-office = rattail_corepos.corepos.office.commands:main',
+ ],
+
+ 'core_office.commands': [
+ 'export-lane-op = rattail_corepos.corepos.office.commands:ExportLaneOp',
],
'crepes.commands': [