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