diff --git a/MANIFEST.in b/MANIFEST.in
index 98448d2..b2e589b 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -3,4 +3,6 @@ include *.txt
include *.rst
include *.md
+recursive-include rattail_corepos/corepos/office/scripts *.php
+
recursive-include rattail_corepos/db/alembic *.py
diff --git a/rattail_corepos/config.py b/rattail_corepos/config.py
index bc3309b..45e3061 100644
--- a/rattail_corepos/config.py
+++ b/rattail_corepos/config.py
@@ -131,6 +131,12 @@ class RattailCOREPOSExtension(ConfigExtension):
config.setdefault('rattail.importing', 'to_corepos_db_lane_op.from_corepos_db_office_op.export.legacy_setting',
'corepos.lane.importing, office.handler')
+ # core-office import-self
+ config.setdefault('rattail.importing', 'to_self.from_corepos_db_office_op.import.default_handler',
+ 'rattail_corepos.corepos.office.importing.db.local:FromCoreOfficeToSelf')
+ config.setdefault('rattail.importing', 'to_self.from_corepos_db_office_op.import.default_cmd',
+ 'core-office import-self')
+
# crepes export-core
config.setdefault('rattail.importing', 'to_corepos_db_office_op.from_corepos_db_office_op.export.default_handler',
'rattail_corepos.corepos.importing.db.corepos:FromCoreToCoreExport')
diff --git a/rattail_corepos/corepos/office/commands.py b/rattail_corepos/corepos/office/commands.py
index 781c4e6..f1356f4 100644
--- a/rattail_corepos/corepos/office/commands.py
+++ b/rattail_corepos/corepos/office/commands.py
@@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
-# Copyright © 2010-2021 Lance Edgar
+# Copyright © 2010-2023 Lance Edgar
#
# This file is part of Rattail.
#
@@ -29,6 +29,7 @@ import sys
from rattail import commands
from rattail_corepos import __version__
from rattail.util import load_object
+from rattail_corepos.corepos.office.util import get_fannie_config_value
def main(*args):
@@ -76,3 +77,29 @@ class ExportLaneOp(commands.ImportSubcommand):
if 'args' in kwargs:
kwargs['dbkey'] = kwargs['args'].dbkey
return kwargs
+
+
+class GetConfigValue(commands.Subcommand):
+ """
+ Get a value from CORE Office `fannie/config.php`
+ """
+ name = 'get-config-value'
+ description = __doc__.strip()
+
+ def add_parser_args(self, parser):
+ parser.add_argument('name', metavar='NAME',
+ help="Name of the config value to get. "
+ "Prefix of `FANNIE_` is not required.")
+
+ def run(self, args):
+ value = get_fannie_config_value(self.config, args.name)
+ self.stdout.write(f"{value}\n")
+
+
+class ImportSelf(commands.ImportSubcommand):
+ """
+ Import data from CORE Office ("op" DB) to "self"
+ """
+ name = 'import-self'
+ description = __doc__.strip()
+ handler_key = 'to_self.from_corepos_db_office_op.import'
diff --git a/rattail_corepos/corepos/office/importing/db/local.py b/rattail_corepos/corepos/office/importing/db/local.py
new file mode 100644
index 0000000..3031735
--- /dev/null
+++ b/rattail_corepos/corepos/office/importing/db/local.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2023 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 -> "self" data import
+"""
+
+from collections import OrderedDict
+
+from corepos.db.office_op import model as corepos
+
+from rattail import importing
+from rattail_corepos.corepos.office.importing import db as corepos_importing
+from rattail_corepos.corepos.office.importing.db.corepos import FromCoreHandler
+from rattail_corepos.corepos.office.util import get_fannie_config_value
+
+
+class FromCoreOfficeToSelf(FromCoreHandler, corepos_importing.model.ToCoreHandler):
+ """
+ Common base class for import handlers which read data from the
+ CORE Office DB for the sake of updating misc. other tables in that
+ same DB.
+ """
+ local_key = 'self'
+
+ def begin_local_transaction(self):
+ self.session = self.host_session
+
+ def rollback_transaction(self):
+ self.rollback_host_transaction()
+
+ def commit_transaction(self):
+ self.commit_host_transaction()
+
+ def get_importers(self):
+ importers = OrderedDict()
+ importers['CustData'] = CustDataImporter
+ return importers
+
+
+class FromCoreOffice(importing.FromSQLAlchemy):
+ """
+ Common base class for the "host" side of importers which read data
+ from the CORE Office DB for the sake of updating misc. other
+ tables in that same DB.
+ """
+
+
+class CustDataImporter(FromCoreOffice, corepos_importing.model.CustDataImporter):
+ """
+ custdata -> custdata
+
+ Primarily used to update the ``blueLine`` field.
+ """
+ host_model_class = corepos.CustData
+ supported_fields = [
+ 'id',
+ 'blue_line',
+ ]
+ allow_create = False
+ allow_delete = False
+
+ def setup(self):
+ super().setup()
+ self.blueline_template = get_fannie_config_value(self.config,
+ 'BLUELINE_TEMPLATE')
+
+ def normalize_host_object(self, customer):
+ blueline = self.blueline_template
+ blueline = blueline.replace('{{ACCOUNTNO}}', str(customer.card_number))
+ blueline = blueline.replace('{{ACCOUNTTYPE}}', customer.member_type.description)
+ blueline = blueline.replace('{{FIRSTNAME}}', customer.first_name or '')
+ blueline = blueline.replace('{{FIRSTINITIAL}}', customer.first_name[0] if customer.first_name else '')
+ blueline = blueline.replace('{{LASTNAME}}', customer.last_name or '')
+ blueline = blueline.replace('{{LASTINITIAL}}', customer.last_name[0] if customer.last_name else '')
+
+ return {
+ 'id': customer.id,
+ 'blue_line': blueline,
+ }
diff --git a/rattail_corepos/corepos/office/importing/db/model.py b/rattail_corepos/corepos/office/importing/db/model.py
index e0170ef..fd2dd55 100644
--- a/rattail_corepos/corepos/office/importing/db/model.py
+++ b/rattail_corepos/corepos/office/importing/db/model.py
@@ -198,12 +198,13 @@ class MemberInfoImporter(ToCore):
data['first_name'] = customer.first_name
data['last_name'] = customer.last_name
- if self.normalize_phone_numbers:
+ if self.normalize_phone_numbers and 'phone' in self.fields:
data['phone'] = self.app.normalize_phone_number(data['phone'])
- if self.strip_address_street or self.strip_address_all:
+ if ((self.strip_address_street or self.strip_address_all)
+ and 'street' in self.fields):
data['street'] = (data['street'] or '').strip()
- if self.strip_address_all:
+ if self.strip_address_all and self.fields_active(['city', 'state', 'zip']):
data['city'] = (data['city'] or '').strip()
data['state'] = (data['state'] or '').strip()
data['zip'] = (data['zip'] or '').strip()
diff --git a/rattail_corepos/corepos/office/scripts/parse-fannie-config.php b/rattail_corepos/corepos/office/scripts/parse-fannie-config.php
new file mode 100644
index 0000000..255f3eb
--- /dev/null
+++ b/rattail_corepos/corepos/office/scripts/parse-fannie-config.php
@@ -0,0 +1,34 @@
+
diff --git a/rattail_corepos/corepos/office/util.py b/rattail_corepos/corepos/office/util.py
new file mode 100644
index 0000000..44aed12
--- /dev/null
+++ b/rattail_corepos/corepos/office/util.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8; -*-
+################################################################################
+#
+# Rattail -- Retail Software Framework
+# Copyright © 2010-2023 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 utility logic
+"""
+
+import json
+import os
+import subprocess
+
+from rattail.files import resource_path
+
+
+def get_fannie_config_value(config, name):
+ """
+ Retrieve a config value from `fannie/config.php`
+
+ :param config: Rattail config object.
+
+ :param name: Name of the config value to be returned. This need
+ not be prefixed with ``FANNIE_`` although it can be.
+
+ :returns: Config value as Python object, e.g. a string or dict.
+ This is believed to work okay but since the Fannie config file
+ represents data with PHP code, which is then converted to JSON
+ and finally to Python, some discrepancies may be possible.
+ """
+ if not name.startswith('FANNIE_'):
+ name = f'FANNIE_{name}'
+
+ is4c = config.require('corepos', 'srcdir')
+ path = os.path.join(is4c, 'fannie', 'config.php')
+ script = resource_path('rattail_corepos.corepos.office:scripts/parse-fannie-config.php')
+
+ try:
+ output = subprocess.check_output(['php', script,
+ '--path', path,
+ '--setting', name])
+ except subprocess.CalledProcessError as error:
+ raise ValueError(f"failed to read value: {error.output.decode('utf_8')}")
+
+ return json.loads(output.decode('utf_8'))
diff --git a/setup.cfg b/setup.cfg
index 898161a..63bf0b8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -42,6 +42,8 @@ console_scripts =
core_office.commands =
export-lane-op = rattail_corepos.corepos.office.commands:ExportLaneOp
+ import-self = rattail_corepos.corepos.office.commands:ImportSelf
+ get-config-value = rattail_corepos.corepos.office.commands:GetConfigValue
crepes.commands =
export-core = rattail_corepos.corepos.commands:ExportCore