From a6306a48821315267ff1557069c8e6bde6761f1d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 30 Dec 2021 21:47:55 -0600 Subject: [PATCH 01/72] Remove deprecation warning for `corepos.db` everything should be using the right imports now --- corepos/db/__init__.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/corepos/db/__init__.py b/corepos/db/__init__.py index 174fee4..db5c837 100644 --- a/corepos/db/__init__.py +++ b/corepos/db/__init__.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2020 Lance Edgar +# Copyright © 2018-2021 Lance Edgar # # This file is part of pyCOREPOS. # @@ -23,12 +23,3 @@ """ Database Interface """ - -from __future__ import unicode_literals, absolute_import - -import warnings -warnings.warn("The `corepos.db` module is deprecated! " - "Please use `corepos.db.office_op` instead.", - DeprecationWarning) - -from corepos.db.office_op import * From b46282264c808792a2492807c8305e2943b9b566 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 17 Jan 2022 18:59:19 -0600 Subject: [PATCH 02/72] Add model for `UserGroup` --- corepos/db/office_op/model.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index ce43a59..c913ae0 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -108,6 +108,24 @@ class TableSyncRule(Base): return "{}: {}".format(self.table_name, self.rule) +class UserGroup(Base): + """ + Represents a user/group assignment. + """ + __tablename__ = 'userGroups' + + group_id = sa.Column('gid', sa.Integer(), nullable=False, + primary_key=True, autoincrement=False) + + name = sa.Column(sa.String(length=50), nullable=True) + + username = sa.Column(sa.String(length=50), nullable=False, + primary_key=True) + + def __str__(self): + return self.name or "" + + class User(Base): """ Represents a user within CORE Office. From af6189b23724c4af9b645090f5a9ee0832925af5 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 2 Mar 2022 21:35:57 -0600 Subject: [PATCH 03/72] Update changelog --- CHANGELOG.md | 5 +++++ corepos/_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 884a776..f83bdb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.7] - 2022-03-02 +### Changed +- Remove deprecation warning for `corepos.db`. +- Add model for `UserGroup`. + ## [0.1.6] - 2021-11-04 ### Changed - Add `User` model for office_op. diff --git a/corepos/_version.py b/corepos/_version.py index 955f1d8..4cdc594 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.6' +__version__ = '0.1.7' From 2b80fd6a6ba5add0ba82fb0ad30ed5768c113b4a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 26 Mar 2022 23:04:05 -0500 Subject: [PATCH 04/72] Add basic `TransactionDetail` for trans archive model --- corepos/db/office_trans/model.py | 10 ++++-- corepos/db/office_trans_archive/__init__.py | 30 ++++++++++++++++ corepos/db/office_trans_archive/model.py | 39 +++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 corepos/db/office_trans_archive/__init__.py create mode 100644 corepos/db/office_trans_archive/model.py diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index 55aff86..7b6240c 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -35,11 +35,10 @@ Base = declarative_base() @six.python_2_unicode_compatible -class TransactionDetail(Base): +class TransactionDetailBase(object): """ Represents a POS transaction detail record. """ - __tablename__ = 'dtransactions' # store store_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False) @@ -123,3 +122,10 @@ class TransactionDetail(Base): def __str__(self): return self.description or '' + + +class TransactionDetail(TransactionDetailBase, Base): + """ + Represents a POS transaction detail record. + """ + __tablename__ = 'dtransactions' diff --git a/corepos/db/office_trans_archive/__init__.py b/corepos/db/office_trans_archive/__init__.py new file mode 100644 index 0000000..e25b200 --- /dev/null +++ b/corepos/db/office_trans_archive/__init__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2022 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +"Archive" Transaction Database Interface +""" + +from sqlalchemy import orm + + +Session = orm.sessionmaker() diff --git a/corepos/db/office_trans_archive/model.py b/corepos/db/office_trans_archive/model.py new file mode 100644 index 0000000..ce70603 --- /dev/null +++ b/corepos/db/office_trans_archive/model.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2022 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +CORE POS Transaction Data Model +""" + +from sqlalchemy.ext.declarative import declarative_base + +from corepos.db.office_trans.model import TransactionDetailBase + + +Base = declarative_base() + + +class TransactionDetail(TransactionDetailBase, Base): + """ + Represents a POS transaction detail record. + """ + __tablename__ = 'bigArchive' From c2723de467a920f9d6b7aea3fed3692eba5502cc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 21 Aug 2022 00:11:24 -0500 Subject: [PATCH 05/72] Delete `productUser` record when `products` record is deleted --- corepos/db/office_op/model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index c913ae0..107da66 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -757,6 +757,7 @@ class ProductUser(Base): backref=orm.backref( 'user_info', uselist=False, + cascade='all, delete-orphan', doc=""" Reference to the :class:`ProductUser` record for this product, if any. """)) From 2444628c13e652ebaebbdbffc497448ba56e2e45 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 2 Jan 2023 16:56:30 -0600 Subject: [PATCH 06/72] Update changelog --- CHANGELOG.md | 5 +++++ corepos/_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f83bdb7..881ced9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.8] - 2023-01-02 +### Changed +- Add basic `TransactionDetail` for trans archive model. +- Delete `productUser` record when `products` record is deleted. + ## [0.1.7] - 2022-03-02 ### Changed - Remove deprecation warning for `corepos.db`. diff --git a/corepos/_version.py b/corepos/_version.py index 4cdc594..4e18c2b 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.7' +__version__ = '0.1.8' From ad5837405f975b31b292ce9d3d7e8b9519103086 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Feb 2023 12:51:50 -0600 Subject: [PATCH 07/72] Require SQLAlchemy 1.4.x --- corepos/db/lane_op/model.py | 7 +++---- corepos/db/office_op/model.py | 5 ++--- corepos/db/office_trans/model.py | 10 +++------- corepos/db/office_trans_archive/model.py | 6 +++--- setup.py | 5 ++--- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py index 80051ab..1c687ab 100644 --- a/corepos/db/lane_op/model.py +++ b/corepos/db/lane_op/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2021 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -25,11 +25,10 @@ Data model for CORE POS "lane_op" DB """ import sqlalchemy as sa -# from sqlalchemy import orm -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import orm -Base = declarative_base() +Base = orm.declarative_base() class Department(Base): diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 107da66..132fd33 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2021 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -29,13 +29,12 @@ import logging import sqlalchemy as sa from sqlalchemy import orm -from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.associationproxy import association_proxy log = logging.getLogger(__name__) -Base = declarative_base() +Base = orm.declarative_base() class StringableDateTime(sa.TypeDecorator): diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index 7b6240c..20c244c 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2020 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -24,17 +24,13 @@ CORE POS Transaction Data Model """ -from __future__ import unicode_literals, absolute_import - -import six import sqlalchemy as sa -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import orm -Base = declarative_base() +Base = orm.declarative_base() -@six.python_2_unicode_compatible class TransactionDetailBase(object): """ Represents a POS transaction detail record. diff --git a/corepos/db/office_trans_archive/model.py b/corepos/db/office_trans_archive/model.py index ce70603..8f4a079 100644 --- a/corepos/db/office_trans_archive/model.py +++ b/corepos/db/office_trans_archive/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2022 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -24,12 +24,12 @@ CORE POS Transaction Data Model """ -from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import orm from corepos.db.office_trans.model import TransactionDetailBase -Base = declarative_base() +Base = orm.declarative_base() class TransactionDetail(TransactionDetailBase, Base): diff --git a/setup.py b/setup.py index 098d0fc..2c5f0f2 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2020 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -63,7 +63,7 @@ requires = [ 'mysql-connector-python', # 8.0.6 'requests', # 2.23.0 'six', # 1.12.0 - 'SQLAlchemy', # 0.9.8 + 'SQLAlchemy>=1.4', # 1.4.0 ] @@ -87,7 +87,6 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Topic :: Office/Business', 'Topic :: Software Development :: Libraries :: Python Modules', ], From 24fb8c8feab6cd276de999f76793485f6460aa89 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 1 May 2023 22:17:38 -0500 Subject: [PATCH 08/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 881ced9..a7925f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.9] - 2023-05-01 +### Changed +- Require SQLAlchemy 1.4.x. + ## [0.1.8] - 2023-01-02 ### Changed - Add basic `TransactionDetail` for trans archive model. diff --git a/corepos/_version.py b/corepos/_version.py index 4e18c2b..def08d5 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.8' +__version__ = '0.1.9' From 6e43449ecbd863ca80975fb0b63b0e74c3019dfc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 16 May 2023 13:16:41 -0500 Subject: [PATCH 09/72] Replace `setup.py` contents with `setup.cfg` --- setup.cfg | 33 +++++++++++++++++++++++++ setup.py | 74 ++----------------------------------------------------- 2 files changed, 35 insertions(+), 72 deletions(-) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..728f179 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,33 @@ +# -*- coding: utf-8; -*- + +[metadata] +name = pyCOREPOS +version = attr: corepos.__version__ +author = Lance Edgar +author_email = lance@edbob.org +url = https://rattailproject.org/ +license = GNU GPL v3 +description = Python Interface to CORE POS +long_description = file: README.rst +classifiers = + Development Status :: 3 - Alpha + Environment :: Console + Environment :: Web Environment + Intended Audience :: Developers + License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) + Natural Language :: English + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Topic :: Office/Business + Topic :: Software Development :: Libraries :: Python Modules + + +[options] +install_requires = + mysql-connector-python + requests + six + SQLAlchemy>=1.4 + +packages = find: diff --git a/setup.py b/setup.py index 2c5f0f2..a02ef90 100644 --- a/setup.py +++ b/setup.py @@ -21,76 +21,6 @@ # ################################################################################ -import os -import sys -from setuptools import setup, find_packages +from setuptools import setup - -here = os.path.abspath(os.path.dirname(__file__)) -exec(open(os.path.join(here, 'corepos', '_version.py')).read()) -README = open(os.path.join(here, 'README.rst')).read() - - -requires = [ - # - # Version numbers within comments below have specific meanings. - # Basically the 'low' value is a "soft low," and 'high' a "soft high." - # In other words: - # - # If either a 'low' or 'high' value exists, the primary point to be - # made about the value is that it represents the most current (stable) - # version available for the package (assuming typical public access - # methods) whenever this project was started and/or documented. - # Therefore: - # - # If a 'low' version is present, you should know that attempts to use - # versions of the package significantly older than the 'low' version - # may not yield happy results. (A "hard" high limit may or may not be - # indicated by a true version requirement.) - # - # Similarly, if a 'high' version is present, and especially if this - # project has laid dormant for a while, you may need to refactor a bit - # when attempting to support a more recent version of the package. (A - # "hard" low limit should be indicated by a true version requirement - # when a 'high' version is present.) - # - # In any case, developers and other users are encouraged to play - # outside the lines with regard to these soft limits. If bugs are - # encountered then they should be filed as such. - # - # package # low high - - 'mysql-connector-python', # 8.0.6 - 'requests', # 2.23.0 - 'six', # 1.12.0 - 'SQLAlchemy>=1.4', # 1.4.0 -] - - -setup( - name = "pyCOREPOS", - version = __version__, - author = "Lance Edgar", - author_email = "lance@edbob.org", - url = "https://rattailproject.org/", - license = "GNU GPL v3", - description = "Python Interface to CORE POS", - long_description = README, - - classifiers = [ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: Office/Business', - 'Topic :: Software Development :: Libraries :: Python Modules', - ], - - install_requires = requires, - packages = find_packages(), -) +setup() From 757fb50a967a624323fe16df0597b8cf237176ae Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 17 May 2023 06:57:14 -0500 Subject: [PATCH 10/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7925f0..7686346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.10] - 2023-05-17 +### Changed +- Replace `setup.py` contents with `setup.cfg`. + ## [0.1.9] - 2023-05-01 ### Changed - Require SQLAlchemy 1.4.x. diff --git a/corepos/_version.py b/corepos/_version.py index def08d5..18c177e 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.9' +__version__ = '0.1.10' From d58426c07369724e8d33fe8dbb41e36a6086eba6 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 22 May 2023 21:34:46 -0500 Subject: [PATCH 11/72] Add support for htdigest auth when using CORE webservices API --- corepos/api.py | 54 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/corepos/api.py b/corepos/api.py index e7310a0..c93ff9d 100644 --- a/corepos/api.py +++ b/corepos/api.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2021 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -28,6 +28,7 @@ import json import logging import requests +from requests.auth import HTTPDigestAuth log = logging.getLogger(__name__) @@ -48,27 +49,42 @@ class CoreAPIError(Exception): class CoreWebAPI(object): """ Client implementation for the CORE webservices API. + + :param str url: URL to the CORE webservices API, + e.g. ``'http://localhost/fannie/ws/'`` + + :param bool verify: How to handle certificate validation for HTTPS + URLs. This value is passed as-is to the ``requests`` library, + so see those docs for more info. The default value for this is + ``True`` because the assumption is that security should be on + by default. Set it to ``False`` in order to disable validation + entirely, e.g. for self-signed certs. (This may also be needed + for basic HTTP URLs?) Other values may be possible also; again + see the ``requests`` docs for more info. + + :param htdigest_username: Username for htdigest authentication, if + applicable. + + :param htdigest_password: Password for htdigest authentication, if + applicable. """ - def __init__(self, url, verify=True): - """ - Constructor for the API client. - - :param str url: URL to the CORE webservices API, - e.g. ``'http://localhost/fannie/ws/'`` - - :param bool verify: How to handle certificate validation for HTTPS - URLs. This value is passed as-is to the ``requests`` library, so - see those docs for more info. The default value for this is - ``True`` because the assumption is that security should be on by - default. Set it to ``False`` in order to disable validation - entirely, e.g. for self-signed certs. (This may also be needed for - basic HTTP URLs?) Other values may be possible also; again see the - ``requests`` docs for more info. - """ + def __init__( + self, + url, + verify=True, + htdigest_username=None, + htdigest_password=None, + ): self.url = url self.verify = verify + self.session = requests.Session() + + if htdigest_username and htdigest_password: + self.session.auth = HTTPDigestAuth(htdigest_username, + htdigest_password) + def post(self, params, method=None): """ Issue a POST request to the API, with the given ``params``. If not @@ -88,8 +104,8 @@ class CoreWebAPI(object): 'id': 1, } - response = requests.post(self.url, data=json.dumps(payload), - verify=self.verify) + response = self.session.post(self.url, data=json.dumps(payload), + verify=self.verify) response.raise_for_status() return response From bb8278dcc8a7c91656958532f300994e34e55233 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 2 Jun 2023 14:26:51 -0500 Subject: [PATCH 12/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7686346..20d31e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.11] - 2023-06-02 +### Changed +- Add support for htdigest auth when using CORE webservices API. + ## [0.1.10] - 2023-05-17 ### Changed - Replace `setup.py` contents with `setup.cfg`. diff --git a/corepos/_version.py b/corepos/_version.py index 18c177e..862eaaa 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.10' +__version__ = '0.1.11' From 852a989bd53516b1371cb1d901d3b33a32cf042f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 6 Jun 2023 13:13:51 -0500 Subject: [PATCH 13/72] Add `get_member_types()` method for CORE API --- corepos/api.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/corepos/api.py b/corepos/api.py index c93ff9d..97ad8c7 100644 --- a/corepos/api.py +++ b/corepos/api.py @@ -132,6 +132,22 @@ class CoreWebAPI(object): assert set(js['result'].keys()) == set(['result']) return js['result']['result'] + def get_member_types(self): + """ + Fetch all Member Type records from CORE. + + :returns: A (potentially empty) list of member type dict records. + """ + params = { + 'entity': 'Memtype', + 'submethod': 'get', + 'columns': {}, + } + + response = self.post(params) + result = self.parse_response(response) + return [json.loads(rec) for rec in result] + def get_members(self): """ Fetch all Member records from CORE. From 4952d2fa3d3e80056f016723e9529c9659d980aa Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Jun 2023 17:28:55 -0500 Subject: [PATCH 14/72] Rename `custdata` model to `CustomerClassic` --- corepos/db/lane_op/model.py | 6 +++++- corepos/db/office_op/model.py | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py index 1c687ab..c7ed5b2 100644 --- a/corepos/db/lane_op/model.py +++ b/corepos/db/lane_op/model.py @@ -209,7 +209,7 @@ class Product(Base): last_sold = sa.Column(sa.DateTime(), nullable=True) -class CustData(Base): +class CustomerClassic(Base): """ Represents a customer of the organization. @@ -275,3 +275,7 @@ class CustData(Base): def __str__(self): return "{} {}".format(self.first_name or '', self.last_name or '').strip() + + +# TODO: deprecate / remove this +CustData = CustomerClassic diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 132fd33..9ec20d2 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1153,7 +1153,7 @@ class Customer(Base): return "{} {}".format(self.first_name or '', self.last_name or '').strip() -class CustData(Base): +class CustomerClassic(Base): """ Represents a customer of the organization. @@ -1219,7 +1219,7 @@ class CustData(Base): member_info = orm.relationship( 'MemberInfo', - primaryjoin='MemberInfo.card_number == CustData.card_number', + primaryjoin='MemberInfo.card_number == CustomerClassic.card_number', foreign_keys=[card_number], uselist=False, back_populates='customers', @@ -1231,6 +1231,10 @@ class CustData(Base): return "{} {}".format(self.first_name or '', self.last_name or '').strip() +# TODO: deprecate / remove this +CustData = CustomerClassic + + class MemberInfo(Base): """ Contact info regarding a member of the organization. @@ -1264,14 +1268,14 @@ class MemberInfo(Base): ads_ok = sa.Column('ads_OK', sa.Boolean(), nullable=True, default=True) customers = orm.relationship( - CustData, - primaryjoin=CustData.card_number == card_number, - order_by=CustData.person_number, - foreign_keys=[CustData.card_number], + CustomerClassic, + primaryjoin=CustomerClassic.card_number == card_number, + order_by=CustomerClassic.person_number, + foreign_keys=[CustomerClassic.card_number], back_populates='member_info', - remote_side=CustData.card_number, + remote_side=CustomerClassic.card_number, doc=""" - List of :class:`CustData` instances which are associated with this member info. + List of :class:`CustomerClassic` instances which are associated with this member info. """) dates = orm.relationship( From c6144ab31025f65e963b8a80a85d5c3d7deb0c0f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Jun 2023 20:38:12 -0500 Subject: [PATCH 15/72] Add note about `meminfo.email_2` field, aka. "alt. phone" --- corepos/db/office_op/model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 9ec20d2..7328525 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1263,7 +1263,10 @@ class MemberInfo(Base): email = sa.Column('email_1', sa.String(length=50), nullable=True) - email2 = sa.Column('email_2', sa.String(length=50), nullable=True) + email2 = sa.Column('email_2', sa.String(length=50), nullable=True, doc=""" + NB. this is labeled "Alt. Phone" in CORE Office member view, and + is named `altPhone` when dealing with CORE Office webservices API. + """) ads_ok = sa.Column('ads_OK', sa.Boolean(), nullable=True, default=True) From b2622c473d669908c989286bde30c0ae873ce098 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Jun 2023 20:39:12 -0500 Subject: [PATCH 16/72] Update changelog --- CHANGELOG.md | 6 ++++++ corepos/_version.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d31e0..58781cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.12] - 2023-06-12 +### Changed +- Add `get_member_types()` method for CORE API. +- Rename model for `custdata` to `CustomerClassic`. +- Add note about `meminfo.email_2` field, aka. "alt. phone". + ## [0.1.11] - 2023-06-02 ### Changed - Add support for htdigest auth when using CORE webservices API. diff --git a/corepos/_version.py b/corepos/_version.py index 862eaaa..002a140 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.11' +__version__ = '0.1.12' From 5a77d14a261dc770a4f13fc3e632065ca8ec48b1 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 18 Jun 2023 11:38:14 -0500 Subject: [PATCH 17/72] Add models for StockPurchase and EquityLiveBalance --- corepos/db/office_trans/model.py | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index 20c244c..d484b4b 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -31,6 +31,41 @@ from sqlalchemy import orm Base = orm.declarative_base() +# TODO: not sure what primary key should be for this? am trying a +# composite one so far, we'll see... +class StockPurchase(Base): + """ + Represents a member equity payment. + """ + __tablename__ = 'stockpurchases' + + card_number = sa.Column('card_no', sa.Integer(), nullable=False, primary_key=True, autoincrement=False) + + amount = sa.Column('stockPurchase', sa.Numeric(precision=10, scale=2), nullable=True) + + datetime = sa.Column('tdate', sa.DateTime(), nullable=True, primary_key=True) + + transaction_number = sa.Column('trans_num', sa.String(length=50), nullable=True, primary_key=True) + + transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True) + + department_number = sa.Column('dept', sa.Integer(), nullable=True) + + def __str__(self): + return f"#{self.card_number} for ${self.amount}" + + +class EquityLiveBalance(Base): + + __tablename__ = 'equity_live_balance' + + member_number = sa.Column('memnum', sa.Integer(), nullable=False, primary_key=True, autoincrement=False) + + payments = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) + + start_date = sa.Column('startdate', sa.DateTime(), nullable=True) + + class TransactionDetailBase(object): """ Represents a POS transaction detail record. From 5921a18f12b9dd3c43e3957aa3561d6a095e5c6b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 2 Sep 2023 13:56:59 -0500 Subject: [PATCH 18/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58781cd..7e949e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.13] - 2023-09-02 +### Changed +- Add models for StockPurchase and EquityLiveBalance. + ## [0.1.12] - 2023-06-12 ### Changed - Add `get_member_types()` method for CORE API. diff --git a/corepos/_version.py b/corepos/_version.py index 002a140..ca4de46 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.12' +__version__ = '0.1.13' From 0e3030394796b39117a2c5d9671188b9bc3db6ad Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 7 Sep 2023 17:45:14 -0500 Subject: [PATCH 19/72] Tweak primary key for StockPurchase model per having a better understanding now, i think.. --- corepos/db/office_trans/model.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index d484b4b..b203240 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -32,7 +32,8 @@ Base = orm.declarative_base() # TODO: not sure what primary key should be for this? am trying a -# composite one so far, we'll see... +# composite one so far, we'll see...cf. also andy's comments in +# https://github.com/CORE-POS/IS4C/pull/1189#issuecomment-1597481138 class StockPurchase(Base): """ Represents a member equity payment. @@ -43,13 +44,13 @@ class StockPurchase(Base): amount = sa.Column('stockPurchase', sa.Numeric(precision=10, scale=2), nullable=True) - datetime = sa.Column('tdate', sa.DateTime(), nullable=True, primary_key=True) + datetime = sa.Column('tdate', sa.DateTime(), nullable=True, primary_key=True, autoincrement=False) transaction_number = sa.Column('trans_num', sa.String(length=50), nullable=True, primary_key=True) transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True) - department_number = sa.Column('dept', sa.Integer(), nullable=True) + department_number = sa.Column('dept', sa.Integer(), nullable=True, primary_key=True, autoincrement=False) def __str__(self): return f"#{self.card_number} for ${self.amount}" From 0dda359094d11627b27807eb88a923f0f2b24e3a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 7 Sep 2023 18:37:48 -0500 Subject: [PATCH 20/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e949e4..9ef75b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.14] - 2023-09-07 +### Changed +- Tweak primary key for StockPurchase model. + ## [0.1.13] - 2023-09-02 ### Changed - Add models for StockPurchase and EquityLiveBalance. diff --git a/corepos/_version.py b/corepos/_version.py index ca4de46..19bf267 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.13' +__version__ = '0.1.14' From 5214db0a83208e37bd85c81e635807fbe6489b36 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 13 Sep 2023 16:23:33 -0500 Subject: [PATCH 21/72] Add model for `CustomerNotifications` table --- corepos/db/office_op/model.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 7328525..68d69d9 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1450,6 +1450,26 @@ class MemberNote(Base): return self.note or "" +class CustomerNotification(Base): + """ + Represents a customer notification for display at the lane. + + https://github.com/CORE-POS/IS4C/blob/master/fannie/classlib2.0/data/models/op/CustomerNotificationsModel.php + """ + __tablename__ = 'CustomerNotifications' + + id = sa.Column('customerNotificationID', sa.Integer(), primary_key=True, autoincrement=True, nullable=False) + card_number = sa.Column('cardNo', sa.Integer(), nullable=True) + customer_id = sa.Column('customerID', sa.Integer(), nullable=True) + source = sa.Column(sa.String(length=50), nullable=True) + type = sa.Column(sa.String(length=50), nullable=True) + message = sa.Column(sa.String(length=255), nullable=True) + modifier_module = sa.Column('modifierModule', sa.String(length=50), nullable=True) + + def __str__(self): + return self.message or '' + + class ReasonCode(Base): """ Reason codes for legacy account suspensions. From 541adb69794a8d4eb92f4d8431ae8b314eb62ba8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 13 Sep 2023 21:51:10 -0500 Subject: [PATCH 22/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ef75b6..4faaeb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.15] - 2023-09-13 +### Changed +- Add model for `CustomerNotifications` table. + ## [0.1.14] - 2023-09-07 ### Changed - Tweak primary key for StockPurchase model. diff --git a/corepos/_version.py b/corepos/_version.py index 19bf267..f877d42 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.14' +__version__ = '0.1.15' From 83eecad28d68871abbcc09ed224b78f3998f747a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 15 Sep 2023 12:43:02 -0500 Subject: [PATCH 23/72] Add model for `office_op.Tender` --- corepos/db/office_op/model.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 68d69d9..f2a6884 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1560,6 +1560,28 @@ class HouseCoupon(Base): return self.description or '' +class Tender(Base): + """ + Represents a tender for payment at POS + """ + __tablename__ = 'tenders' + + tender_id = sa.Column('TenderID', sa.Integer(), primary_key=True, nullable=False) + + tender_code = sa.Column('TenderCode', sa.String(length=2), nullable=True) + tender_name = sa.Column('TenderName', sa.String(length=25), nullable=True) + tender_type = sa.Column('TenderType', sa.String(length=2), nullable=True) + change_message = sa.Column('ChangeMessage', sa.String(length=25), nullable=True) + min_amount = sa.Column('MinAmount', sa.Numeric(precision=10, scale=2), nullable=True) + max_amount = sa.Column('MaxAmount', sa.Numeric(precision=10, scale=2), nullable=True) + max_refund = sa.Column('MaxRefund', sa.Numeric(precision=10, scale=2), nullable=True) + tender_module = sa.Column('TenderModule', sa.String(length=50), nullable=True) + sales_code = sa.Column('SalesCode', sa.Integer(), nullable=True) + + def __str__(self): + return self.tender_name or '' + + class BatchType(Base): """ Represents the definition of a batch type. From f06f236a604bb10a1e8d9f92f676647d19532b5b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 15 Sep 2023 12:59:52 -0500 Subject: [PATCH 24/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4faaeb5..56b70c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.16] - 2023-09-15 +### Changed +- Add model for `office_op.Tender`. + ## [0.1.15] - 2023-09-13 ### Changed - Add model for `CustomerNotifications` table. diff --git a/corepos/_version.py b/corepos/_version.py index f877d42..05c3021 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.15' +__version__ = '0.1.16' From e5988102ad13c03f5d516dca1245b3959fc852ef Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 5 Oct 2023 11:52:39 -0500 Subject: [PATCH 25/72] Rename module to `corepos.db.office_arch` --- corepos/db/office_arch/__init__.py | 30 ++++++++++++++++ corepos/db/office_arch/model.py | 39 +++++++++++++++++++++ corepos/db/office_trans_archive/__init__.py | 10 +++--- corepos/db/office_trans_archive/model.py | 17 +++------ 4 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 corepos/db/office_arch/__init__.py create mode 100644 corepos/db/office_arch/model.py diff --git a/corepos/db/office_arch/__init__.py b/corepos/db/office_arch/__init__.py new file mode 100644 index 0000000..70292e9 --- /dev/null +++ b/corepos/db/office_arch/__init__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2023 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +"Archive" Transaction Database Interface +""" + +from sqlalchemy import orm + + +Session = orm.sessionmaker() diff --git a/corepos/db/office_arch/model.py b/corepos/db/office_arch/model.py new file mode 100644 index 0000000..bf01a4d --- /dev/null +++ b/corepos/db/office_arch/model.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2023 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +CORE Office "arch" data model +""" + +from sqlalchemy import orm + +from corepos.db.office_trans.model import TransactionDetailBase + + +Base = orm.declarative_base() + + +class TransactionDetail(TransactionDetailBase, Base): + """ + Represents a POS transaction detail record. + """ + __tablename__ = 'bigArchive' diff --git a/corepos/db/office_trans_archive/__init__.py b/corepos/db/office_trans_archive/__init__.py index e25b200..6ddb31d 100644 --- a/corepos/db/office_trans_archive/__init__.py +++ b/corepos/db/office_trans_archive/__init__.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2022 Lance Edgar +# Copyright © 2018-2023 Lance Edgar # # This file is part of pyCOREPOS. # @@ -24,7 +24,9 @@ "Archive" Transaction Database Interface """ -from sqlalchemy import orm +import warnings +warnings.warn("The `corepos.db.office_trans_archive` module is deprecated! " + "Please use `corepos.db.office_arch` instead.", + DeprecationWarning, stacklevel=2) - -Session = orm.sessionmaker() +from corepos.db.office_arch import * diff --git a/corepos/db/office_trans_archive/model.py b/corepos/db/office_trans_archive/model.py index 8f4a079..e57d071 100644 --- a/corepos/db/office_trans_archive/model.py +++ b/corepos/db/office_trans_archive/model.py @@ -24,16 +24,9 @@ CORE POS Transaction Data Model """ -from sqlalchemy import orm +import warnings +warnings.warn("The `corepos.db.office_trans_archive.model` module is deprecated! " + "Please use `corepos.db.office_arch.model` instead.", + DeprecationWarning, stacklevel=2) -from corepos.db.office_trans.model import TransactionDetailBase - - -Base = orm.declarative_base() - - -class TransactionDetail(TransactionDetailBase, Base): - """ - Represents a POS transaction detail record. - """ - __tablename__ = 'bigArchive' +from corepos.db.office_arch.model import * From 7171531dce6cd465c9a14f29f2437c829c21021d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 7 Oct 2023 18:58:17 -0500 Subject: [PATCH 26/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b70c3..247807d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.17] - 2023-10-07 +### Changed +- Rename module to `corepos.db.office_arch`. + ## [0.1.16] - 2023-09-15 ### Changed - Add model for `office_op.Tender`. diff --git a/corepos/_version.py b/corepos/_version.py index 05c3021..9a17824 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.16' +__version__ = '0.1.17' From 3a3fba19e461a9acbf75410c25bb333494674c2d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 11 Oct 2023 18:36:12 -0500 Subject: [PATCH 27/72] Fix the `Department.tax_rate` relationship whoops i mistook `dept_tax` for a boolean previously --- corepos/db/office_op/model.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index f2a6884..c99f6a8 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -225,12 +225,18 @@ class Department(Base): Represents a department within the organization. """ __tablename__ = 'departments' + __table_args__ = ( + sa.ForeignKeyConstraint(['dept_tax'], ['taxrates.id']), + ) number = sa.Column('dept_no', sa.SmallInteger(), primary_key=True, autoincrement=False, nullable=False) name = sa.Column('dept_name', sa.String(length=30), nullable=True) - tax = sa.Column('dept_tax', sa.Boolean(), nullable=True) + tax_rate_id = sa.Column('dept_tax', sa.SmallInteger(), nullable=True) + tax_rate = orm.relationship('TaxRate') + # TODO: deprecate / remove this + tax = orm.synonym('tax_rate_id') food_stampable = sa.Column('dept_fs', sa.Boolean(), nullable=True) From 1597c163c6f40dfd97887b8e78560186f4e16cb0 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 12 Oct 2023 10:34:13 -0500 Subject: [PATCH 28/72] Let `MemberInfo.dates` be an object, not a list --- corepos/db/office_op/model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index c99f6a8..f6787bf 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1292,6 +1292,7 @@ class MemberInfo(Base): primaryjoin='MemberDate.card_number == MemberInfo.card_number', foreign_keys='MemberDate.card_number', cascade='all, delete-orphan', + uselist=False, doc=""" List of date records for the member. """, From 67dd9777ba827806cf95afd5bb47a7ce05923e70 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 12 Oct 2023 10:38:44 -0500 Subject: [PATCH 29/72] Update changelog --- CHANGELOG.md | 5 +++++ corepos/_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 247807d..8e0fbd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.18] - 2023-10-12 +### Changed +- Fix the `Department.tax_rate` relationship. +- Let `MemberInfo.dates` be an object, not a list. + ## [0.1.17] - 2023-10-07 ### Changed - Rename module to `corepos.db.office_arch`. diff --git a/corepos/_version.py b/corepos/_version.py index 9a17824..a6dbf47 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.17' +__version__ = '0.1.18' From f217f00a8f409cc1a29ab965da3d79f383a4da43 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 20 Oct 2023 14:31:13 -0500 Subject: [PATCH 30/72] Fix data types for tax, voided in `dtransactions` --- corepos/db/office_trans/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index b203240..ce5304b 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -120,7 +120,8 @@ class TransactionDetailBase(object): reg_price = sa.Column('regPrice', sa.Numeric(precision=10, scale=2), nullable=True) - tax = sa.Column(sa.Boolean(), nullable=True) + tax = sa.Column(sa.SmallInteger(), nullable=True) + tax_rate_id = orm.synonym('tax') food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True) @@ -132,7 +133,7 @@ class TransactionDetailBase(object): discount_type = sa.Column('discounttype', sa.Integer(), nullable=True) - voided = sa.Column(sa.Boolean(), nullable=True) + voided = sa.Column(sa.Integer(), nullable=True) percent_discount = sa.Column('percentDiscount', sa.Integer(), nullable=True) From 909d75796b40b7b1e527ef621c733799aeb9f9d4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 20 Oct 2023 19:03:20 -0500 Subject: [PATCH 31/72] Fix synonym for `dtransactions.tax` --- corepos/db/office_trans/model.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index ce5304b..2848af5 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -26,6 +26,7 @@ CORE POS Transaction Data Model import sqlalchemy as sa from sqlalchemy import orm +from sqlalchemy.ext.declarative import declared_attr Base = orm.declarative_base() @@ -121,7 +122,10 @@ class TransactionDetailBase(object): reg_price = sa.Column('regPrice', sa.Numeric(precision=10, scale=2), nullable=True) tax = sa.Column(sa.SmallInteger(), nullable=True) - tax_rate_id = orm.synonym('tax') + + @declared_attr + def tax_rate_id(self): + return orm.synonym('tax') food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True) From 7cd89029ac1d5461d026f1d4e70ecb498a26aa6f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 1 Nov 2023 08:15:37 -0500 Subject: [PATCH 32/72] Update changelog --- CHANGELOG.md | 5 +++++ corepos/_version.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e0fbd0..a52aee7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.19] - 2023-11-01 +### Changed +- Fix data types for tax, voided in `dtransactions`. +- Fix synonym for `dtransactions.tax`. + ## [0.1.18] - 2023-10-12 ### Changed - Fix the `Department.tax_rate` relationship. diff --git a/corepos/_version.py b/corepos/_version.py index a6dbf47..560cfb5 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.18' +__version__ = '0.1.19' From df6f0d97937272b4ae6f07eabba6262b4b0367b8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 16 May 2024 19:13:35 -0500 Subject: [PATCH 33/72] Add enum for CORE (Office) DB types for use with typer commands --- corepos/enum.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/corepos/enum.py b/corepos/enum.py index 55619d8..28e33ea 100644 --- a/corepos/enum.py +++ b/corepos/enum.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2019 Lance Edgar +# Copyright © 2018-2024 Lance Edgar # # This file is part of pyCOREPOS. # @@ -24,12 +24,14 @@ CORE POS enumeration constants """ -from __future__ import unicode_literals, absolute_import +from collections import OrderedDict +from enum import Enum -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict + +class CoreDbType(str, Enum): + office_op = 'office_op' + office_trans = 'office_trans' + office_arch = 'office_arch' BATCH_DISCOUNT_TYPE_PRICE_CHANGE = 0 From 777b198e4441aa3ca88b92453a1b445012bf2441 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 29 May 2024 10:09:15 -0500 Subject: [PATCH 34/72] Update changelog --- CHANGELOG.md | 4 ++++ corepos/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a52aee7..f9feb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.1.20] - 2024-05-29 +### Changed +- Add enum for CORE (Office) DB types. + ## [0.1.19] - 2023-11-01 ### Changed - Fix data types for tax, voided in `dtransactions`. diff --git a/corepos/_version.py b/corepos/_version.py index 560cfb5..0275ddd 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.19' +__version__ = '0.1.20' From f80d03daaa301f2b98a482945e0bfa6726dbe571 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 29 May 2024 10:10:25 -0500 Subject: [PATCH 35/72] Fix default dist filename for release task not sure why this fix was needed, did setuptools behavior change? --- tasks.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks.py b/tasks.py index a9b9fc3..9156d43 100644 --- a/tasks.py +++ b/tasks.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2020 Lance Edgar +# Copyright © 2018-2024 Lance Edgar # # This file is part of pyCOREPOS. # @@ -35,10 +35,10 @@ exec(open(os.path.join(here, 'corepos', '_version.py')).read()) @task -def release(ctx): +def release(c): """ Release a new version of 'pyCOREPOS'. """ shutil.rmtree('pyCOREPOS.egg-info') - ctx.run('python setup.py sdist --formats=gztar') - ctx.run('twine upload dist/pyCOREPOS-{}.tar.gz'.format(__version__)) + c.run('python setup.py sdist --formats=gztar') + c.run('twine upload dist/pycorepos-{}.tar.gz'.format(__version__)) From 5cf8d2ac05917f73001f9cdfd32fcfc74305cd56 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 10 Jun 2024 15:55:14 -0500 Subject: [PATCH 36/72] feat: switch from setup.cfg to pyproject.toml + hatchling --- .gitignore | 3 +++ corepos/_version.py | 5 ++++- pyproject.toml | 50 +++++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 33 ------------------------------ setup.py | 26 ----------------------- tasks.py | 13 +++++++++++- 6 files changed, 69 insertions(+), 61 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore index 9a52e5b..07ddefb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ +*~ +*.pyc +dist/ pyCOREPOS.egg-info/ diff --git a/corepos/_version.py b/corepos/_version.py index 0275ddd..555fee3 100644 --- a/corepos/_version.py +++ b/corepos/_version.py @@ -1,3 +1,6 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.20' +from importlib.metadata import version + + +__version__ = version('pyCOREPOS') diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4701f62 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,50 @@ + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + + +[project] +name = "pyCOREPOS" +version = "0.1.20" +description = "Python Interface to CORE POS" +readme = "README.rst" +authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] +license = {text = "GNU GPL v3+"} +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Office/Business", + "Topic :: Software Development :: Libraries :: Python Modules", +] + +dependencies = [ + "mysql-connector-python", + "requests", + "six", + "SQLAlchemy>=1.4", +] + + +[project.urls] +Homepage = "https://rattailproject.org" +Repository = "https://kallithea.rattailproject.org/rattail-project/pycorepos" +Issues = "https://redmine.rattailproject.org/projects/corepos-integration/issues" +Changelog = "https://kallithea.rattailproject.org/rattail-project/pycorepos/files/master/CHANGELOG.md" + + +[tool.commitizen] +version_provider = "pep621" +tag_format = "v$version" +update_changelog_on_bump = true + + +[tool.hatch.build.targets.wheel] +packages = ["corepos"] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 728f179..0000000 --- a/setup.cfg +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8; -*- - -[metadata] -name = pyCOREPOS -version = attr: corepos.__version__ -author = Lance Edgar -author_email = lance@edbob.org -url = https://rattailproject.org/ -license = GNU GPL v3 -description = Python Interface to CORE POS -long_description = file: README.rst -classifiers = - Development Status :: 3 - Alpha - Environment :: Console - Environment :: Web Environment - Intended Audience :: Developers - License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) - Natural Language :: English - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 3 - Topic :: Office/Business - Topic :: Software Development :: Libraries :: Python Modules - - -[options] -install_requires = - mysql-connector-python - requests - six - SQLAlchemy>=1.4 - -packages = find: diff --git a/setup.py b/setup.py deleted file mode 100644 index a02ef90..0000000 --- a/setup.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8; -*- -################################################################################ -# -# pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2023 Lance Edgar -# -# This file is part of pyCOREPOS. -# -# pyCOREPOS 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. -# -# pyCOREPOS 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 -# pyCOREPOS. If not, see . -# -################################################################################ - -from setuptools import setup - -setup() diff --git a/tasks.py b/tasks.py index 9156d43..caf7777 100644 --- a/tasks.py +++ b/tasks.py @@ -25,13 +25,24 @@ Tasks for 'pyCOREPOS' package """ import os +import re import shutil from invoke import task here = os.path.abspath(os.path.dirname(__file__)) -exec(open(os.path.join(here, 'corepos', '_version.py')).read()) +__version__ = None +pattern = re.compile(r'^version = "(\d+\.\d+\.\d+)"$') +with open(os.path.join(here, 'pyproject.toml'), 'rt') as f: + for line in f: + line = line.rstrip('\n') + match = pattern.match(line) + if match: + __version__ = match.group(1) + break +if not __version__: + raise RuntimeError("could not parse version!") @task From 04948fb840e19d31a16d144a00256cf6c9229221 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 10 Jun 2024 15:55:51 -0500 Subject: [PATCH 37/72] =?UTF-8?q?bump:=20version=200.1.20=20=E2=86=92=200.?= =?UTF-8?q?2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9feb94..d40d286 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.2.0 (2024-06-10) + +### Feat + +- switch from setup.cfg to pyproject.toml + hatchling + ## [0.1.20] - 2024-05-29 ### Changed - Add enum for CORE (Office) DB types. diff --git a/pyproject.toml b/pyproject.toml index 4701f62..2f3c924 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.1.20" +version = "0.2.0" description = "Python Interface to CORE POS" readme = "README.rst" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From 40e647d5c8c8d756b29d460e37125e9dbbf7e74e Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 10 Jun 2024 15:57:09 -0500 Subject: [PATCH 38/72] build: use newer convention when building for release --- tasks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index caf7777..6946a7f 100644 --- a/tasks.py +++ b/tasks.py @@ -50,6 +50,7 @@ def release(c): """ Release a new version of 'pyCOREPOS'. """ - shutil.rmtree('pyCOREPOS.egg-info') - c.run('python setup.py sdist --formats=gztar') + if os.path.exists('pyCOREPOS.egg-info'): + shutil.rmtree('pyCOREPOS.egg-info') + c.run('python -m build --sdist') c.run('twine upload dist/pycorepos-{}.tar.gz'.format(__version__)) From 3b54dbd0686c5afe30fe7780665f27aacd0bd5c3 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 14 Jun 2024 19:48:40 -0500 Subject: [PATCH 39/72] docs: use more specific project homepage url --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2f3c924..b5c6fb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ [project.urls] -Homepage = "https://rattailproject.org" +Homepage = "https://redmine.rattailproject.org/projects/corepos-integration" Repository = "https://kallithea.rattailproject.org/rattail-project/pycorepos" Issues = "https://redmine.rattailproject.org/projects/corepos-integration/issues" Changelog = "https://kallithea.rattailproject.org/rattail-project/pycorepos/files/master/CHANGELOG.md" From e9638c73a4c7c8ac832eeb72b1731bb422998d13 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 1 Jul 2024 16:32:39 -0500 Subject: [PATCH 40/72] fix: remove dependency for `six` package --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b5c6fb0..1e356fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ classifiers = [ dependencies = [ "mysql-connector-python", "requests", - "six", "SQLAlchemy>=1.4", ] From 66cf108b3f1f52c3bb2ac965abd924a35250739d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 4 Jul 2024 13:18:45 -0500 Subject: [PATCH 41/72] fix: remove `Change` data model since it isn't actually part of CORE, and is now handled in other ways with regard to datasync --- corepos/db/office_op/model.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index f6787bf..d5da2f7 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2023 Lance Edgar +# Copyright © 2018-2024 Lance Edgar # # This file is part of pyCOREPOS. # @@ -54,23 +54,6 @@ class StringableDateTime(sa.TypeDecorator): raise NotImplementedError -class Change(Base): - """ - Represents a changed (or deleted) record, which is pending synchronization - to another system(s). - - .. note:: - This table may or may not be installed to a given CORE Office Op DB. Its - presence is required if Rattail datasync needs to "watch" the DB. - """ - __tablename__ = 'datasync_changes' - - id = sa.Column(sa.Integer(), nullable=False, primary_key=True) - object_type = sa.Column(sa.String(length=255), nullable=False) - object_key = sa.Column(sa.String(length=255), nullable=False) - deleted = sa.Column(sa.Boolean(), nullable=False, default=False) - - class Parameter(Base): """ Represents a "parameter" value. From bcff5605559c64a83197c413ff754c94177ce0ee Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 4 Jul 2024 18:27:50 -0500 Subject: [PATCH 42/72] fix: add API methods, `get_employees()` and `get_employee()` --- corepos/api.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/corepos/api.py b/corepos/api.py index 97ad8c7..a24b906 100644 --- a/corepos/api.py +++ b/corepos/api.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2023 Lance Edgar +# Copyright © 2018-2024 Lance Edgar # # This file is part of pyCOREPOS. # @@ -198,6 +198,40 @@ class CoreWebAPI(object): if result: return result + def get_employees(self, **columns): + """ + Fetch some or all of Employee records from CORE. + + :returns: A (potentially empty) list of employee dict records. + """ + params = { + 'entity': 'Employees', + 'submethod': 'get', + 'columns': columns, + } + response = self.post(params) + result = self.parse_response(response) + return [json.loads(rec) for rec in result] + + def get_employee(self, emp_no, **columns): + """ + Fetch an existing Employee record from CORE. + + :returns: Either a employee dict record, or ``None``. + """ + columns['emp_no'] = emp_no + params = { + 'entity': 'Employees', + 'submethod': 'get', + 'columns': columns, + } + response = self.post(params) + result = self.parse_response(response) + if result: + if len(result) > 1: + log.warning("CORE API returned %s employee results", len(result)) + return json.loads(result[0]) + def get_stores(self, **columns): """ Fetch some or all of Store records from CORE. From 2df38854204d32b3a34c4d96e8e86b5a6fdc53bd Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 4 Jul 2024 23:59:49 -0500 Subject: [PATCH 43/72] =?UTF-8?q?bump:=20version=200.2.0=20=E2=86=92=200.2?= =?UTF-8?q?.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d40d286..e97814c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.2.1 (2024-07-04) + +### Fix + +- add API methods, `get_employees()` and `get_employee()` +- remove `Change` data model +- remove dependency for `six` package + ## v0.2.0 (2024-06-10) ### Feat diff --git a/pyproject.toml b/pyproject.toml index 1e356fd..29dc012 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.2.0" +version = "0.2.1" description = "Python Interface to CORE POS" readme = "README.rst" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From 27a46ed18f72cc721f65861408dc0905205368de Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 6 Aug 2024 10:38:01 -0500 Subject: [PATCH 44/72] feat: add model for `CustomReceiptLine` (`op.customReceipt`) --- corepos/db/office_op/model.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index d5da2f7..33c8313 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1572,6 +1572,20 @@ class Tender(Base): return self.tender_name or '' +class CustomReceiptLine(Base): + """ + Represents a "text string" line for a custom receipt. + """ + __tablename__ = 'customReceipt' + + sequence = sa.Column('seq', sa.Integer(), primary_key=True, autoincrement=True, nullable=False) + type = sa.Column(sa.String(length=20), primary_key=True, autoincrement=False, nullable=False) + text = sa.Column(sa.String(length=80), nullable=True) + + def __str__(self): + return self.text or "" + + class BatchType(Base): """ Represents the definition of a batch type. From b5b29cdcf1f0a5c5c04b7b70430cf7cc37623be4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 6 Aug 2024 11:34:07 -0500 Subject: [PATCH 45/72] feat: add model for `MemberContactPreference` (`op.memContactPrefs`) --- corepos/db/office_op/model.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 33c8313..5f6fc3d 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1378,7 +1378,7 @@ class MemberDate(Base): class MemberContact(Base): """ - Contact preferences for members + Member contacts """ __tablename__ = 'memContact' @@ -1405,6 +1405,19 @@ class MemberContact(Base): return str(self.preference) +class MemberContactPreference(Base): + """ + Member contact preferences + """ + __tablename__ = 'memContactPrefs' + + id = sa.Column('pref_id', sa.Integer(), primary_key=True, autoincrement=False, nullable=False) + description = sa.Column('pref_description', sa.String(length=50), nullable=True) + + def __str__(self): + return self.description or "" + + class MemberBarcode(Base): """ Additional barcode for a member. From b425095a8da82f065e0c7c25de24f67c3af0b2b4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 6 Aug 2024 23:21:26 -0500 Subject: [PATCH 46/72] =?UTF-8?q?bump:=20version=200.2.1=20=E2=86=92=200.3?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e97814c..f6e7048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.0 (2024-08-06) + +### Feat + +- add model for `MemberContactPreference` (`op.memContactPrefs`) +- add model for `CustomReceiptLine` (`op.customReceipt`) + ## v0.2.1 (2024-07-04) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 29dc012..58ee2a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.2.1" +version = "0.3.0" description = "Python Interface to CORE POS" readme = "README.rst" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From b44e5a3d6062fbbc1a7ee009022c3e380f918c8f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 13 Sep 2024 18:49:14 -0500 Subject: [PATCH 47/72] docs: use markdown for readme file --- README.md | 5 +++++ README.rst | 7 ------- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd69797 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ + +# pyCOREPOS + +A Python interface to the [CORE POS](https://github.com/CORE-POS) +system. diff --git a/README.rst b/README.rst deleted file mode 100644 index 0b34d2f..0000000 --- a/README.rst +++ /dev/null @@ -1,7 +0,0 @@ - -pyCOREPOS -========= - -A Python interface to the `CORE POS`_ system. - -.. _CORE POS: https://github.com/CORE-POS diff --git a/pyproject.toml b/pyproject.toml index 58ee2a2..de0f54b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "hatchling.build" name = "pyCOREPOS" version = "0.3.0" description = "Python Interface to CORE POS" -readme = "README.rst" +readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] license = {text = "GNU GPL v3+"} classifiers = [ From ed48f9134a841756a455143adccc269b807f5592 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 14 Sep 2024 10:43:39 -0500 Subject: [PATCH 48/72] docs: update project links to forgejo --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index de0f54b..869d6fb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,10 +33,10 @@ dependencies = [ [project.urls] -Homepage = "https://redmine.rattailproject.org/projects/corepos-integration" -Repository = "https://kallithea.rattailproject.org/rattail-project/pycorepos" -Issues = "https://redmine.rattailproject.org/projects/corepos-integration/issues" -Changelog = "https://kallithea.rattailproject.org/rattail-project/pycorepos/files/master/CHANGELOG.md" +Homepage = "https://forgejo.wuttaproject.org/rattail/pycorepos" +Repository = "https://forgejo.wuttaproject.org/rattail/pycorepos" +Issues = "https://forgejo.wuttaproject.org/rattail/pycorepos/issues" +Changelog = "https://forgejo.wuttaproject.org/rattail/pycorepos/src/branch/master/CHANGELOG.md" [tool.commitizen] From 3bb01f23979db35065276d91b0bc058d113d7c19 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 17 Dec 2024 16:45:11 -0600 Subject: [PATCH 49/72] fix: add `wicable`, `active` columns for Department model --- corepos/db/office_op/model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 5f6fc3d..8590375 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -223,6 +223,10 @@ class Department(Base): food_stampable = sa.Column('dept_fs', sa.Boolean(), nullable=True) + wicable = sa.Column('dept_wicable', sa.SmallInteger(), nullable=True) + + active = sa.Column(sa.Boolean(), default=True) + limit = sa.Column('dept_limit', sa.Float(), nullable=True) minimum = sa.Column('dept_minimum', sa.Float(), nullable=True) From 310a261b48d93701199b6326aec4be3d277f3809 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 17 Dec 2024 16:45:36 -0600 Subject: [PATCH 50/72] =?UTF-8?q?bump:=20version=200.3.0=20=E2=86=92=200.3?= =?UTF-8?q?.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6e7048..6c20cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.1 (2024-12-17) + +### Fix + +- add `wicable`, `active` columns for Department model + ## v0.3.0 (2024-08-06) ### Feat diff --git a/pyproject.toml b/pyproject.toml index 869d6fb..0bbc23a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.3.0" +version = "0.3.1" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From cb6ed15eb8c162a9aa9a4c4644a8105a6d8fdc6b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 11 Jan 2025 21:51:28 -0600 Subject: [PATCH 51/72] fix: add model for `MasterSuperDepartment` --- corepos/db/office_op/model.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 8590375..12688af 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -169,6 +169,25 @@ class Store(Base): return self.description or "" +class MasterSuperDepartment(Base): + """ + A department may belong to more than one superdepartment, but has + one "master" superdepartment. This avoids duplicating rows in + some reports. By convention, a department's "master" + superdepartment is the one with the lowest superID. + """ + __tablename__ = 'MasterSuperDepts' + + super_id = sa.Column('superID', sa.Integer(), primary_key=True, autoincrement=False, nullable=False) + + department_id = sa.Column('dept_ID', sa.Integer(), primary_key=True, autoincrement=False, nullable=False) + + super_name = sa.Column(sa.String(length=50), nullable=True) + + def __str__(self): + return self.super_name or "" + + class SuperDepartment(Base): """ Represents a "super" (parent/child) department mapping. From 464f107a88ed996dc5d627206f55a3d41fa453cf Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 11 Jan 2025 21:52:22 -0600 Subject: [PATCH 52/72] fix: add `MemberType.ignore_sales` column --- corepos/db/office_op/model.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 12688af..5e9a34a 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1037,6 +1037,9 @@ class MemberType(Base): ssi = sa.Column(sa.Boolean(), nullable=True) + ignoreSales = sa.Column(sa.Boolean(), nullable=True, default=False) + ignore_sales = orm.synonym('ignoreSales') + # TODO: this was apparently added "recently" - isn't present in all DBs # (need to figure out how to conditionally include it in model?) # sales_code = sa.Column('salesCode', sa.Integer(), nullable=True) From 47c23a65aee63fa4018d3b66d6610a837e5aea8a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 11 Jan 2025 21:55:54 -0600 Subject: [PATCH 53/72] fix: add base class for all transaction tables, views also rename some classes; add `dlogBig` for archive view --- corepos/db/office_arch/model.py | 19 +++++++++++++---- corepos/db/office_trans/model.py | 36 +++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/corepos/db/office_arch/model.py b/corepos/db/office_arch/model.py index bf01a4d..49b0bbd 100644 --- a/corepos/db/office_arch/model.py +++ b/corepos/db/office_arch/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2023 Lance Edgar +# Copyright © 2018-2024 Lance Edgar # # This file is part of pyCOREPOS. # @@ -26,14 +26,25 @@ CORE Office "arch" data model from sqlalchemy import orm -from corepos.db.office_trans.model import TransactionDetailBase +from corepos.db.office_trans.model import DTransactionBase, DLogBase Base = orm.declarative_base() -class TransactionDetail(TransactionDetailBase, Base): +class BigArchive(DTransactionBase, Base): """ - Represents a POS transaction detail record. + Represents a record from ``bigArchive`` table. """ __tablename__ = 'bigArchive' + + +# TODO: deprecate / remove this +TransactionDetail = BigArchive + + +class DLogBig(DLogBase, Base): + """ + Represents a record from ``dlogBig`` view. + """ + __tablename__ = 'dlogBig' diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index 2848af5..b4659cd 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2023 Lance Edgar +# Copyright © 2018-2024 Lance Edgar # # This file is part of pyCOREPOS. # @@ -68,7 +68,7 @@ class EquityLiveBalance(Base): start_date = sa.Column('startdate', sa.DateTime(), nullable=True) -class TransactionDetailBase(object): +class TransactionDetailBase: """ Represents a POS transaction detail record. """ @@ -86,10 +86,12 @@ class TransactionDetailBase(object): transaction_number = sa.Column('trans_no', sa.Integer(), nullable=True) transaction_type = sa.Column('trans_type', sa.String(length=1), nullable=True) transaction_subtype = sa.Column('trans_subtype', sa.String(length=2), nullable=True) - transaction_status = sa.Column('trans_status', sa.String(length=1), nullable=True) - # timestamps - date_time = sa.Column('datetime', sa.DateTime(), nullable=True) + trans_status = sa.Column(sa.String(length=1), nullable=True) + + @declared_attr + def transaction_status(self): + return orm.synonym('trans_status') # cashier employee_number = sa.Column('emp_no', sa.Integer(), nullable=True) @@ -115,7 +117,11 @@ class TransactionDetailBase(object): cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - unit_price = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True) + unitPrice = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True) + + @declared_attr + def unit_price(self): + return orm.synonym('unitPrice') total = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) @@ -161,8 +167,22 @@ class TransactionDetailBase(object): return self.description or '' -class TransactionDetail(TransactionDetailBase, Base): +class DTransactionBase(TransactionDetailBase): + + date_time = sa.Column('datetime', sa.DateTime(), nullable=True) + + +class DLogBase(TransactionDetailBase): + + date_time = sa.Column('tdate', sa.DateTime(), nullable=True) + + +class DTransaction(DTransactionBase, Base): """ - Represents a POS transaction detail record. + Represents a record from ``dtransactions`` table. """ __tablename__ = 'dtransactions' + + +# TODO: deprecate / remove this +TransactionDetail = DTransaction From 465e565f7b975219647dfbe548187584cdd74192 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 11 Jan 2025 22:01:38 -0600 Subject: [PATCH 54/72] =?UTF-8?q?bump:=20version=200.3.1=20=E2=86=92=200.3?= =?UTF-8?q?.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 ++++++++ pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c20cbc..ad1845a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.2 (2025-01-11) + +### Fix + +- add base class for all transaction tables, views +- add `MemberType.ignore_sales` column +- add model for `MasterSuperDepartment` + ## v0.3.1 (2024-12-17) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 0bbc23a..80bf76b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.3.1" +version = "0.3.2" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From 023d826d316ccd87035a8fe1384a662712224894 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 13 Jan 2025 12:57:41 -0600 Subject: [PATCH 55/72] fix: remove `autoincrement` option for composite PK fields --- corepos/db/office_op/model.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 5e9a34a..16bbc57 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -80,7 +80,9 @@ class TableSyncRule(Base): """ __tablename__ = 'TableSyncRules' - id = sa.Column('tableSyncRuleID', sa.Integer(), nullable=False, primary_key=True, autoincrement=True) + # nb. this should be autoincrement, but we can't do that + # automatically via sqlalchemy when PK is composite + id = sa.Column('tableSyncRuleID', sa.Integer(), nullable=False, primary_key=True) table_name = sa.Column('tableName', sa.String(length=255), nullable=False, primary_key=True) @@ -1617,8 +1619,11 @@ class CustomReceiptLine(Base): """ __tablename__ = 'customReceipt' - sequence = sa.Column('seq', sa.Integer(), primary_key=True, autoincrement=True, nullable=False) - type = sa.Column(sa.String(length=20), primary_key=True, autoincrement=False, nullable=False) + # nb. this should be autoincrement, but we can't do that + # automatically via sqlalchemy when PK is composite + sequence = sa.Column('seq', sa.Integer(), primary_key=True, nullable=False) + + type = sa.Column(sa.String(length=20), primary_key=True, nullable=False) text = sa.Column(sa.String(length=80), nullable=True) def __str__(self): From e37cf88cd91ace61ba27b0bdea53bdfca4488d38 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 13 Jan 2025 13:31:21 -0600 Subject: [PATCH 56/72] =?UTF-8?q?bump:=20version=200.3.2=20=E2=86=92=200.3?= =?UTF-8?q?.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad1845a..ccc62cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.3 (2025-01-13) + +### Fix + +- remove `autoincrement` option for composite PK fields + ## v0.3.2 (2025-01-11) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 80bf76b..a59e830 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.3.2" +version = "0.3.3" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From ab56a35acc3c08ee6d9992dee59086d8654c987c Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 14 Jan 2025 20:20:41 -0600 Subject: [PATCH 57/72] fix: add more enums for batch discount type, editor UI also fix column type for editor UI --- corepos/db/office_op/model.py | 2 +- corepos/enum.py | 23 +++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 16bbc57..68627c0 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1646,7 +1646,7 @@ class BatchType(Base): special_order_eligible = sa.Column('specialOrderEligible', sa.Boolean(), nullable=True, default=True) - editor_ui = sa.Column('editorUI', sa.Boolean(), nullable=True, default=True) + editor_ui = sa.Column('editorUI', sa.SmallInteger(), nullable=True, default=True) allow_single_store = sa.Column('allowSingleStore', sa.Boolean(), nullable=True, default=False) diff --git a/corepos/enum.py b/corepos/enum.py index 28e33ea..93780c3 100644 --- a/corepos/enum.py +++ b/corepos/enum.py @@ -34,14 +34,33 @@ class CoreDbType(str, Enum): office_arch = 'office_arch' +BATCH_DISCOUNT_TYPE_TRACKING = -1 BATCH_DISCOUNT_TYPE_PRICE_CHANGE = 0 BATCH_DISCOUNT_TYPE_SALE_EVERYONE = 1 BATCH_DISCOUNT_TYPE_SALE_RESTRICTED = 2 +BATCH_DISCOUNT_TYPE_SLIDING_PERCENT = 3 +BATCH_DISCOUNT_TYPE_SLIDING_AMOUNT = 5 BATCH_DISCOUNT_TYPE = OrderedDict([ - (BATCH_DISCOUNT_TYPE_PRICE_CHANGE, "Price Change"), + (BATCH_DISCOUNT_TYPE_PRICE_CHANGE, "None (Change regular price)"), (BATCH_DISCOUNT_TYPE_SALE_EVERYONE, "Sale for everyone"), - (BATCH_DISCOUNT_TYPE_SALE_RESTRICTED, "Member/Owner only sale"), + (BATCH_DISCOUNT_TYPE_SALE_RESTRICTED, "Sale for Members"), + (BATCH_DISCOUNT_TYPE_SLIDING_PERCENT, "Sliding % Off for Members"), + (BATCH_DISCOUNT_TYPE_SLIDING_AMOUNT, "Sliding $ Off for Members"), + (BATCH_DISCOUNT_TYPE_TRACKING, "Tracking (does not change any prices)"), +]) + + +BATCH_EDITOR_UI_STANDARD = 1 +BATCH_EDITOR_UI_PAIRED_SALE = 2 +BATCH_EDITOR_UI_PARTIAL = 3 +BATCH_EDITOR_UI_TRACKING = 4 + +BATCH_EDITOR_UI = OrderedDict([ + (BATCH_EDITOR_UI_STANDARD, "Standard"), + (BATCH_EDITOR_UI_PAIRED_SALE, "Paired Sale"), + (BATCH_EDITOR_UI_PARTIAL, "Partial"), + (BATCH_EDITOR_UI_TRACKING, "Tracking"), ]) From 01852ceecc437ac7b5f5c429d7d1f5487c40daa4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 08:45:59 -0600 Subject: [PATCH 58/72] fix: misc. cleanup for sales batch models --- corepos/db/office_op/model.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 68627c0..6087f67 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1636,7 +1636,9 @@ class BatchType(Base): """ __tablename__ = 'batchType' - id = sa.Column('batchTypeID', sa.Integer(), primary_key=True, autoincrement=False, nullable=False) + # nb. this is *not* autoincrement for some reason; must + # calculate new ID manually based on max existing + id = sa.Column('batchTypeID', sa.Integer(), nullable=False, primary_key=True, autoincrement=False) description = sa.Column('typeDesc', sa.String(length=50), nullable=True) @@ -1661,9 +1663,6 @@ class Batch(Base): Represents a batch. """ __tablename__ = 'batches' - __table_args__ = ( - sa.ForeignKeyConstraint(['batchType'], ['batchType.batchTypeID']), - ) id = sa.Column('batchID', sa.Integer(), primary_key=True, autoincrement=True, nullable=False) @@ -1673,7 +1672,8 @@ class Batch(Base): name = sa.Column('batchName', sa.String(length=80), nullable=True) - batch_type_id = sa.Column('batchType', sa.Integer(), nullable=True) + batch_type_id = sa.Column('batchType', sa.Integer(), + sa.ForeignKey('batchType.batchTypeID'), nullable=True) batch_type = orm.relationship(BatchType) discount_type = sa.Column('discountType', sa.Integer(), nullable=True) @@ -1695,16 +1695,18 @@ class BatchItem(Base): Represents a batch "list" item. """ __tablename__ = 'batchList' - __table_args__ = ( - sa.ForeignKeyConstraint(['batchID'], ['batches.batchID']), - ) id = sa.Column('listID', sa.Integer(), primary_key=True, autoincrement=True, nullable=False) - batch_id = sa.Column('batchID', sa.Integer(), nullable=True) + batch_id = sa.Column('batchID', sa.Integer(), + sa.ForeignKey('batches.batchID'), nullable=True) batch = orm.relationship(Batch, backref=orm.backref('items')) upc = sa.Column(sa.String(length=13), nullable=True) + product = orm.relationship( + Product, + primaryjoin=Product.upc == upc, + foreign_keys=[upc]) sale_price = sa.Column('salePrice', sa.Numeric(precision=9, scale=3), nullable=True) From b8ca60b508a2e3689ee7061be0d2c15841f07a64 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 08:51:40 -0600 Subject: [PATCH 59/72] =?UTF-8?q?bump:=20version=200.3.3=20=E2=86=92=200.3?= =?UTF-8?q?.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 +++++++ pyproject.toml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccc62cb..39309fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.4 (2025-01-15) + +### Fix + +- misc. cleanup for sales batch models +- add more enums for batch discount type, editor UI + ## v0.3.3 (2025-01-13) ### Fix diff --git a/pyproject.toml b/pyproject.toml index a59e830..143843f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.3.3" +version = "0.3.4" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From 7aaa35dac70b059f2d43eb1b05d98a8b2963e51d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 10:59:14 -0600 Subject: [PATCH 60/72] fix: add workaround to avoid missing schema columns more columns will need to be added to this workaround i'm sure, but this takes care of a couple small ones --- corepos/db/office_op/model.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 6087f67..4d79e88 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1039,12 +1039,11 @@ class MemberType(Base): ssi = sa.Column(sa.Boolean(), nullable=True) - ignoreSales = sa.Column(sa.Boolean(), nullable=True, default=False) - ignore_sales = orm.synonym('ignoreSales') + # nb. this must be added explicitly if DB is new enough + #ignore_sales = sa.Column('ignoreSales', sa.Boolean(), nullable=True, default=False) - # TODO: this was apparently added "recently" - isn't present in all DBs - # (need to figure out how to conditionally include it in model?) - # sales_code = sa.Column('salesCode', sa.Integer(), nullable=True) + # nb. this must be added explicitly if DB is new enough + #sales_code = sa.Column('salesCode', sa.Integer(), nullable=True) def __str__(self): return self.description or "" @@ -1842,3 +1841,22 @@ class PurchaseOrderNote(Base): def __str__(self): return self.notes or "" + + +# the rest of this is a workaround to deal with the fact that some +# CORE databases have columns which others do not. i had assumed that +# all would be more or less the same but not so in practice. so if +# your DB *does* have these columns, you must invoke the function +# below in order to merge them into your schema. you should do this +# on app startup and they'll be available normally from then on. + +RUNTIME = {'added_latest_columns': False} + +def use_latest_columns(): + if RUNTIME['added_latest_columns']: + return + + MemberType.ignore_sales = sa.Column('ignoreSales', sa.Boolean(), nullable=True, default=False) + MemberType.sales_code = sa.Column('salesCode', sa.Integer(), nullable=True) + + RUNTIME['added_latest_columns'] = True From 6c1fc9a8031a7dc8c8efd682349922f3915bf4e2 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 11:00:22 -0600 Subject: [PATCH 61/72] =?UTF-8?q?bump:=20version=200.3.4=20=E2=86=92=200.3?= =?UTF-8?q?.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39309fe..bba74ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.5 (2025-01-15) + +### Fix + +- add workaround to avoid missing schema columns + ## v0.3.4 (2025-01-15) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 143843f..2aaef92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.3.4" +version = "0.3.5" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From d21346bbffb618594d630e5230589c69839c2891 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 14:48:18 -0600 Subject: [PATCH 62/72] fix: fix ordering of name columns for MemberInfo so they show up correctly by default e.g. in UI --- corepos/db/office_op/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 4d79e88..ff3f493 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -1259,14 +1259,14 @@ class MemberInfo(Base): card_number = sa.Column('card_no', sa.Integer(), primary_key=True, autoincrement=False, nullable=False) - last_name = sa.Column(sa.String(length=30), nullable=True) - first_name = sa.Column(sa.String(length=30), nullable=True) - other_last_name = sa.Column('othlast_name', sa.String(length=30), nullable=True) + last_name = sa.Column(sa.String(length=30), nullable=True) other_first_name = sa.Column('othfirst_name', sa.String(length=30), nullable=True) + other_last_name = sa.Column('othlast_name', sa.String(length=30), nullable=True) + street = sa.Column(sa.String(length=255), nullable=True) city = sa.Column(sa.String(length=20), nullable=True) From 97fb0b28cba228ef435b954a5a9784e0f547f212 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 19:14:13 -0600 Subject: [PATCH 63/72] feat: add common base class for `dtransactions` and similar models more work to be done here i'm sure, but this should hopefully be a conservative change in the right direction --- corepos/db/common/__init__.py | 0 corepos/db/common/trans.py | 123 ++++++++++++++++++++++++++++++ corepos/db/lane_trans/__init__.py | 30 ++++++++ corepos/db/lane_trans/model.py | 62 +++++++++++++++ corepos/db/office_arch/model.py | 21 ++++- corepos/db/office_trans/model.py | 111 ++------------------------- 6 files changed, 239 insertions(+), 108 deletions(-) create mode 100644 corepos/db/common/__init__.py create mode 100644 corepos/db/common/trans.py create mode 100644 corepos/db/lane_trans/__init__.py create mode 100644 corepos/db/lane_trans/model.py diff --git a/corepos/db/common/__init__.py b/corepos/db/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/corepos/db/common/trans.py b/corepos/db/common/trans.py new file mode 100644 index 0000000..2b4e503 --- /dev/null +++ b/corepos/db/common/trans.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2025 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +Common schema for transaction data models +""" + +import sqlalchemy as sa +from sqlalchemy import orm +from sqlalchemy.ext.declarative import declared_attr + + +class TransactionDetailBase: + """ + Base class for POS transaction detail models, shared by Office + + Lane. + """ + + # register + register_number = sa.Column('register_no', sa.Integer(), nullable=True) + + # txn + transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True) + transaction_number = sa.Column('trans_no', sa.Integer(), nullable=True) + transaction_type = sa.Column('trans_type', sa.String(length=1), nullable=True) + transaction_subtype = sa.Column('trans_subtype', sa.String(length=2), nullable=True) + trans_status = sa.Column(sa.String(length=1), nullable=True) + + @declared_attr + def transaction_status(self): + return orm.synonym('trans_status') + + # cashier + employee_number = sa.Column('emp_no', sa.Integer(), nullable=True) + + # customer + card_number = sa.Column('card_no', sa.Integer(), nullable=True) + member_type = sa.Column('memType', sa.Integer(), nullable=True) + staff = sa.Column(sa.Boolean(), nullable=True) + + ############################## + # remainder is "line item" ... + ############################## + + upc = sa.Column(sa.String(length=13), nullable=True) + + department_number = sa.Column('department', sa.Integer(), nullable=True) + + description = sa.Column(sa.String(length=30), nullable=True) + + quantity = sa.Column(sa.Float(), nullable=True) + + scale = sa.Column(sa.Boolean(), nullable=True, default=False) + + cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) + + unitPrice = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True) + + @declared_attr + def unit_price(self): + return orm.synonym('unitPrice') + + total = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) + + reg_price = sa.Column('regPrice', sa.Numeric(precision=10, scale=2), nullable=True) + + tax = sa.Column(sa.SmallInteger(), nullable=True) + + @declared_attr + def tax_rate_id(self): + return orm.synonym('tax') + + food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True) + + discount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) + + member_discount = sa.Column('memDiscount', sa.Numeric(precision=10, scale=2), nullable=True) + + discountable = sa.Column(sa.Boolean(), nullable=True) + + discount_type = sa.Column('discounttype', sa.Integer(), nullable=True) + + voided = sa.Column(sa.Integer(), nullable=True) + + percent_discount = sa.Column('percentDiscount', sa.Integer(), nullable=True) + + item_quantity = sa.Column('ItemQtty', sa.Float(), nullable=True) + + volume_discount_type = sa.Column('volDiscType', sa.Integer(), nullable=True) + + volume = sa.Column(sa.Integer(), nullable=True) + + volume_special = sa.Column('VolSpecial', sa.Numeric(precision=10, scale=2), nullable=True) + + mix_match = sa.Column('mixMatch', sa.String(length=13), nullable=True) + + matched = sa.Column(sa.Boolean(), nullable=True) + + num_flag = sa.Column('numflag', sa.Integer(), nullable=True, default=0) + + char_flag = sa.Column('charflag', sa.String(length=2), nullable=True) + + def __str__(self): + return self.description or '' diff --git a/corepos/db/lane_trans/__init__.py b/corepos/db/lane_trans/__init__.py new file mode 100644 index 0000000..8e8c706 --- /dev/null +++ b/corepos/db/lane_trans/__init__.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2025 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +Lane Transaction Database +""" + +from sqlalchemy import orm + + +Session = orm.sessionmaker() diff --git a/corepos/db/lane_trans/model.py b/corepos/db/lane_trans/model.py new file mode 100644 index 0000000..b656d8f --- /dev/null +++ b/corepos/db/lane_trans/model.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2025 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +Data model for CORE POS "lane_trans" DB +""" + +import sqlalchemy as sa +from sqlalchemy import orm + +from corepos.db.common import trans as common + + +Base = orm.declarative_base() + + +class DTransactionBase(common.TransactionDetailBase): + """ + Base class for ``dtransactions`` and similar models. + """ + pos_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False) + + store_id = sa.Column(sa.Integer(), nullable=True, default=0) + date_time = sa.Column('datetime', sa.DateTime(), nullable=True) + + +class DTransaction(DTransactionBase, Base): + """ + Represents a record from ``dtransactions`` table. + """ + __tablename__ = 'dtransactions' + + +class LocalTempTrans(common.TransactionDetailBase, Base): + """ + Represents a record from ``localtemptrans`` table. + """ + __tablename__ = 'localtemptrans' + __table_args__ = ( + sa.PrimaryKeyConstraint('trans_id'), + ) + + date_time = sa.Column('datetime', sa.DateTime(), nullable=True) diff --git a/corepos/db/office_arch/model.py b/corepos/db/office_arch/model.py index 49b0bbd..bc5838f 100644 --- a/corepos/db/office_arch/model.py +++ b/corepos/db/office_arch/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2024 Lance Edgar +# Copyright © 2018-2025 Lance Edgar # # This file is part of pyCOREPOS. # @@ -24,9 +24,11 @@ CORE Office "arch" data model """ +import sqlalchemy as sa from sqlalchemy import orm -from corepos.db.office_trans.model import DTransactionBase, DLogBase +from corepos.db.common import trans as common +from corepos.db.office_trans.model import DTransactionBase Base = orm.declarative_base() @@ -34,7 +36,7 @@ Base = orm.declarative_base() class BigArchive(DTransactionBase, Base): """ - Represents a record from ``bigArchive`` table. + Data model for ``bigArchive`` table. """ __tablename__ = 'bigArchive' @@ -43,8 +45,19 @@ class BigArchive(DTransactionBase, Base): TransactionDetail = BigArchive +class DLogBase(common.TransactionDetailBase): + """ + Base class for ``dlogBig`` and similar models. + """ + store_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False) + + store_id = sa.Column(sa.Integer(), nullable=True, default=0) + pos_row_id = sa.Column(sa.Integer(), nullable=True) + date_time = sa.Column('tdate', sa.DateTime(), nullable=True) + + class DLogBig(DLogBase, Base): """ - Represents a record from ``dlogBig`` view. + Data model for ``dlogBig`` view. """ __tablename__ = 'dlogBig' diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py index b4659cd..c2b0959 100644 --- a/corepos/db/office_trans/model.py +++ b/corepos/db/office_trans/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2024 Lance Edgar +# Copyright © 2018-2025 Lance Edgar # # This file is part of pyCOREPOS. # @@ -26,7 +26,8 @@ CORE POS Transaction Data Model import sqlalchemy as sa from sqlalchemy import orm -from sqlalchemy.ext.declarative import declared_attr + +from corepos.db.common import trans as common Base = orm.declarative_base() @@ -68,118 +69,20 @@ class EquityLiveBalance(Base): start_date = sa.Column('startdate', sa.DateTime(), nullable=True) -class TransactionDetailBase: +class DTransactionBase(common.TransactionDetailBase): """ - Represents a POS transaction detail record. + Base class for ``dtransactions`` and similar models. """ - - # store store_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False) - store_id = sa.Column(sa.Integer(), nullable=True, default=0) - # register - register_number = sa.Column('register_no', sa.Integer(), nullable=True) pos_row_id = sa.Column(sa.Integer(), nullable=True) - - # txn - transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True) - transaction_number = sa.Column('trans_no', sa.Integer(), nullable=True) - transaction_type = sa.Column('trans_type', sa.String(length=1), nullable=True) - transaction_subtype = sa.Column('trans_subtype', sa.String(length=2), nullable=True) - - trans_status = sa.Column(sa.String(length=1), nullable=True) - - @declared_attr - def transaction_status(self): - return orm.synonym('trans_status') - - # cashier - employee_number = sa.Column('emp_no', sa.Integer(), nullable=True) - - # customer - card_number = sa.Column('card_no', sa.Integer(), nullable=True) - member_type = sa.Column('memType', sa.Integer(), nullable=True) - staff = sa.Column(sa.Boolean(), nullable=True) - - ############################## - # remainder is "line item" ... - ############################## - - upc = sa.Column(sa.String(length=13), nullable=True) - - department_number = sa.Column('department', sa.Integer(), nullable=True) - - description = sa.Column(sa.String(length=30), nullable=True) - - quantity = sa.Column(sa.Float(), nullable=True) - - scale = sa.Column(sa.Boolean(), nullable=True, default=False) - - cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - - unitPrice = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True) - - @declared_attr - def unit_price(self): - return orm.synonym('unitPrice') - - total = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - - reg_price = sa.Column('regPrice', sa.Numeric(precision=10, scale=2), nullable=True) - - tax = sa.Column(sa.SmallInteger(), nullable=True) - - @declared_attr - def tax_rate_id(self): - return orm.synonym('tax') - - food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True) - - discount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - - member_discount = sa.Column('memDiscount', sa.Numeric(precision=10, scale=2), nullable=True) - - discountable = sa.Column(sa.Boolean(), nullable=True) - - discount_type = sa.Column('discounttype', sa.Integer(), nullable=True) - - voided = sa.Column(sa.Integer(), nullable=True) - - percent_discount = sa.Column('percentDiscount', sa.Integer(), nullable=True) - - item_quantity = sa.Column('ItemQtty', sa.Float(), nullable=True) - - volume_discount_type = sa.Column('volDiscType', sa.Integer(), nullable=True) - - volume = sa.Column(sa.Integer(), nullable=True) - - volume_special = sa.Column('VolSpecial', sa.Numeric(precision=10, scale=2), nullable=True) - - mix_match = sa.Column('mixMatch', sa.String(length=13), nullable=True) - - matched = sa.Column(sa.Boolean(), nullable=True) - - num_flag = sa.Column('numflag', sa.Integer(), nullable=True, default=0) - - char_flag = sa.Column('charflag', sa.String(length=2), nullable=True) - - def __str__(self): - return self.description or '' - - -class DTransactionBase(TransactionDetailBase): - + store_id = sa.Column(sa.Integer(), nullable=True, default=0) date_time = sa.Column('datetime', sa.DateTime(), nullable=True) -class DLogBase(TransactionDetailBase): - - date_time = sa.Column('tdate', sa.DateTime(), nullable=True) - - class DTransaction(DTransactionBase, Base): """ - Represents a record from ``dtransactions`` table. + Data model for ``dtransactions`` table. """ __tablename__ = 'dtransactions' From c3b639390d1abfc3b13c0a5e4313b3161997de8f Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 19:21:00 -0600 Subject: [PATCH 64/72] fix: add `Employee` model for lane_op with abstract common base schema --- corepos/db/common/op.py | 56 +++++++++++++++++++++++++++++++++++ corepos/db/lane_op/model.py | 11 ++++++- corepos/db/office_op/model.py | 31 ++++--------------- 3 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 corepos/db/common/op.py diff --git a/corepos/db/common/op.py b/corepos/db/common/op.py new file mode 100644 index 0000000..7cf84fc --- /dev/null +++ b/corepos/db/common/op.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# pyCOREPOS -- Python Interface to CORE POS +# Copyright © 2018-2025 Lance Edgar +# +# This file is part of pyCOREPOS. +# +# pyCOREPOS 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. +# +# pyCOREPOS 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 +# pyCOREPOS. If not, see . +# +################################################################################ +""" +Common schema for operational data models +""" + +import sqlalchemy as sa + + +class EmployeeBase: + """ + Base class for Employee models, shared by Office + Lane. + """ + number = sa.Column('emp_no', sa.SmallInteger(), nullable=False, + primary_key=True, autoincrement=False) + + cashier_password = sa.Column('CashierPassword', sa.String(length=50), nullable=True) + + admin_password = sa.Column('AdminPassword', sa.String(length=50), nullable=True) + + first_name = sa.Column('FirstName', sa.String(length=255), nullable=True) + + last_name = sa.Column('LastName', sa.String(length=255), nullable=True) + + job_title = sa.Column('JobTitle', sa.String(length=255), nullable=True) + + active = sa.Column('EmpActive', sa.Boolean(), nullable=True) + + frontend_security = sa.Column('frontendsecurity', sa.SmallInteger(), nullable=True) + + backend_security = sa.Column('backendsecurity', sa.SmallInteger(), nullable=True) + + birth_date = sa.Column('birthdate', sa.DateTime(), nullable=True) + + def __str__(self): + return ' '.join([self.first_name or '', self.last_name or '']).strip() diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py index c7ed5b2..08da4f2 100644 --- a/corepos/db/lane_op/model.py +++ b/corepos/db/lane_op/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2023 Lance Edgar +# Copyright © 2018-2025 Lance Edgar # # This file is part of pyCOREPOS. # @@ -27,10 +27,19 @@ Data model for CORE POS "lane_op" DB import sqlalchemy as sa from sqlalchemy import orm +from corepos.db.common import op as common + Base = orm.declarative_base() +class Employee(common.EmployeeBase, Base): + """ + Data model for ``employees`` table. + """ + __tablename__ = 'employees' + + class Department(Base): """ Represents a department within the organization. diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index ff3f493..67f405a 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -2,7 +2,7 @@ ################################################################################ # # pyCOREPOS -- Python Interface to CORE POS -# Copyright © 2018-2024 Lance Edgar +# Copyright © 2018-2025 Lance Edgar # # This file is part of pyCOREPOS. # @@ -31,6 +31,8 @@ import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.ext.associationproxy import association_proxy +from corepos.db.common import op as common + log = logging.getLogger(__name__) @@ -991,35 +993,12 @@ class ProductPhysicalLocation(Base): location = sa.Column(sa.SmallInteger(), nullable=True, default=0) -class Employee(Base): +class Employee(common.EmployeeBase, Base): """ - Represents an employee within the organization. + Data model for ``employees`` table. """ __tablename__ = 'employees' - number = sa.Column('emp_no', sa.SmallInteger(), primary_key=True, autoincrement=False, nullable=False) - - cashier_password = sa.Column('CashierPassword', sa.String(length=50), nullable=True) - - admin_password = sa.Column('AdminPassword', sa.String(length=50), nullable=True) - - first_name = sa.Column('FirstName', sa.String(length=255), nullable=True) - - last_name = sa.Column('LastName', sa.String(length=255), nullable=True) - - job_title = sa.Column('JobTitle', sa.String(length=255), nullable=True) - - active = sa.Column('EmpActive', sa.Boolean(), nullable=True) - - frontend_security = sa.Column('frontendsecurity', sa.SmallInteger(), nullable=True) - - backend_security = sa.Column('backendsecurity', sa.SmallInteger(), nullable=True) - - birth_date = sa.Column('birthdate', sa.DateTime(), nullable=True) - - def __str__(self): - return ' '.join([self.first_name or '', self.last_name or '']).strip() - class MemberType(Base): """ From 28cb23adc4a60cc7a3f9c37fa906281a45487f64 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 19:59:18 -0600 Subject: [PATCH 65/72] =?UTF-8?q?bump:=20version=200.3.5=20=E2=86=92=200.4?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bba74ba..ca4e4bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.4.0 (2025-01-24) + +### Feat + +- add common base class for `dtransactions` and similar models + +### Fix + +- add `Employee` model for lane_op +- fix ordering of name columns for MemberInfo + ## v0.3.5 (2025-01-15) ### Fix diff --git a/pyproject.toml b/pyproject.toml index 2aaef92..75ef20f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.3.5" +version = "0.4.0" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From 50351596acaced2b5b14fd4ad4f3488f9d09b111 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 20:18:13 -0600 Subject: [PATCH 66/72] fix: add model for lane_trans `LocalTrans` --- corepos/db/lane_trans/model.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/corepos/db/lane_trans/model.py b/corepos/db/lane_trans/model.py index b656d8f..f2245f5 100644 --- a/corepos/db/lane_trans/model.py +++ b/corepos/db/lane_trans/model.py @@ -26,6 +26,7 @@ Data model for CORE POS "lane_trans" DB import sqlalchemy as sa from sqlalchemy import orm +from sqlalchemy.ext.declarative import declared_attr from corepos.db.common import trans as common @@ -45,18 +46,34 @@ class DTransactionBase(common.TransactionDetailBase): class DTransaction(DTransactionBase, Base): """ - Represents a record from ``dtransactions`` table. + Data model for ``dtransactions`` table. """ __tablename__ = 'dtransactions' -class LocalTempTrans(common.TransactionDetailBase, Base): +class LocalTransBase(common.TransactionDetailBase): """ - Represents a record from ``localtemptrans`` table. + Base class for ``localtrans`` and similar models. """ - __tablename__ = 'localtemptrans' - __table_args__ = ( - sa.PrimaryKeyConstraint('trans_id'), - ) + + @declared_attr + def __table_args__(self): + return ( + sa.PrimaryKeyConstraint('trans_id'), + ) date_time = sa.Column('datetime', sa.DateTime(), nullable=True) + + +class LocalTrans(LocalTransBase, Base): + """ + Data model for ``localtrans`` table. + """ + __tablename__ = 'localtrans' + + +class LocalTempTrans(LocalTransBase, Base): + """ + Data model for ``localtemptrans`` table. + """ + __tablename__ = 'localtemptrans' From 2fe089bd57b93388908a7b5adff3709ebde2e0fb Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 25 Jan 2025 16:41:17 -0600 Subject: [PATCH 67/72] fix: add `Parameter` model for lane_op --- corepos/db/common/op.py | 18 ++++++++++++++++++ corepos/db/lane_op/model.py | 7 +++++++ corepos/db/office_op/model.py | 17 ++--------------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/corepos/db/common/op.py b/corepos/db/common/op.py index 7cf84fc..c0d40f5 100644 --- a/corepos/db/common/op.py +++ b/corepos/db/common/op.py @@ -27,6 +27,24 @@ Common schema for operational data models import sqlalchemy as sa +class ParameterBase: + """ + Base class for Parameter models, shared by Office + Lane. + """ + store_id = sa.Column(sa.SmallInteger(), primary_key=True, nullable=False) + + lane_id = sa.Column(sa.SmallInteger(), primary_key=True, nullable=False) + + param_key = sa.Column(sa.String(length=100), primary_key=True, nullable=False) + + param_value = sa.Column(sa.String(length=255), nullable=True) + + is_array = sa.Column(sa.Boolean(), nullable=True) + + def __str__(self): + return f"{self.store_id}-{self.lane_id} {self.param_key}" + + class EmployeeBase: """ Base class for Employee models, shared by Office + Lane. diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py index 08da4f2..3074dd1 100644 --- a/corepos/db/lane_op/model.py +++ b/corepos/db/lane_op/model.py @@ -33,6 +33,13 @@ from corepos.db.common import op as common Base = orm.declarative_base() +class Parameter(common.ParameterBase, Base): + """ + Data model for ``parameters`` table. + """ + __tablename__ = 'parameters' + + class Employee(common.EmployeeBase, Base): """ Data model for ``employees`` table. diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index 67f405a..ad4b89b 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -56,25 +56,12 @@ class StringableDateTime(sa.TypeDecorator): raise NotImplementedError -class Parameter(Base): +class Parameter(common.ParameterBase, Base): """ - Represents a "parameter" value. + Data model for ``parameters`` table. """ __tablename__ = 'parameters' - store_id = sa.Column(sa.SmallInteger(), primary_key=True, nullable=False) - - lane_id = sa.Column(sa.SmallInteger(), primary_key=True, nullable=False) - - param_key = sa.Column(sa.String(length=100), primary_key=True, nullable=False) - - param_value = sa.Column(sa.String(length=255), nullable=True) - - is_array = sa.Column(sa.Boolean(), nullable=True) - - def __str__(self): - return "{}-{} {}".format(self.store_id, self.lane_id, self.param_key) - class TableSyncRule(Base): """ From a2a1d7faee837c0a0216693a635fe5f237ce6366 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 25 Jan 2025 16:58:17 -0600 Subject: [PATCH 68/72] fix: define common base schema for Product model --- corepos/db/common/op.py | 99 +++++++++++++++++++++++++ corepos/db/lane_op/model.py | 134 +--------------------------------- corepos/db/office_op/model.py | 99 ++----------------------- 3 files changed, 109 insertions(+), 223 deletions(-) diff --git a/corepos/db/common/op.py b/corepos/db/common/op.py index c0d40f5..30ecc1c 100644 --- a/corepos/db/common/op.py +++ b/corepos/db/common/op.py @@ -72,3 +72,102 @@ class EmployeeBase: def __str__(self): return ' '.join([self.first_name or '', self.last_name or '']).strip() + + +class ProductBase: + """ + Base class for Product models, shared by Office + Lane. + """ + id = sa.Column(sa.Integer(), nullable=False, primary_key=True, autoincrement=True) + + upc = sa.Column(sa.String(length=13), nullable=True) + + description = sa.Column(sa.String(length=30), nullable=True) + + brand = sa.Column(sa.String(length=30), nullable=True) + + formatted_name = sa.Column(sa.String(length=30), nullable=True) + + normal_price = sa.Column(sa.Float(), nullable=True) + + price_method = sa.Column('pricemethod', sa.SmallInteger(), nullable=True) + + group_price = sa.Column('groupprice', sa.Float(), nullable=True) + + quantity = sa.Column(sa.SmallInteger(), nullable=True) + + special_price = sa.Column(sa.Float(), nullable=True) + + special_price_method = sa.Column('specialpricemethod', sa.SmallInteger(), nullable=True) + + special_group_price = sa.Column('specialgroupprice', sa.Float(), nullable=True) + + special_quantity = sa.Column('specialquantity', sa.SmallInteger(), nullable=True) + + special_limit = sa.Column(sa.SmallInteger(), nullable=True) + + start_date = sa.Column(sa.DateTime(), nullable=True) + + end_date = sa.Column(sa.DateTime(), nullable=True) + + department_number = sa.Column('department', sa.SmallInteger(), nullable=True) + + size = sa.Column(sa.String(length=9), nullable=True) + + tax_rate_id = sa.Column('tax', sa.SmallInteger(), nullable=True) + + foodstamp = sa.Column(sa.Boolean(), nullable=True) + + scale = sa.Column(sa.Boolean(), nullable=True) + + scale_price = sa.Column('scaleprice', sa.Float(), nullable=True) + + mix_match_code = sa.Column('mixmatchcode', sa.String(length=13), nullable=True) + + created = sa.Column(sa.DateTime(), nullable=True) + + modified = sa.Column(sa.DateTime(), nullable=True) + + tare_weight = sa.Column('tareweight', sa.Float(), nullable=True) + + discount = sa.Column(sa.SmallInteger(), nullable=True) + + discount_type = sa.Column('discounttype', sa.SmallInteger(), nullable=True) + + line_item_discountable = sa.Column(sa.Boolean(), nullable=True) + + unit_of_measure = sa.Column('unitofmeasure', sa.String(length=15), nullable=True) + + wicable = sa.Column(sa.SmallInteger(), nullable=True) + + quantity_enforced = sa.Column('qttyEnforced', sa.Boolean(), nullable=True) + + id_enforced = sa.Column('idEnforced', sa.SmallInteger(), nullable=True) + + cost = sa.Column(sa.Float(), nullable=True) + + special_cost = sa.Column(sa.Float(), nullable=True) + + received_cost = sa.Column(sa.Float(), nullable=True) + + in_use = sa.Column('inUse', sa.Boolean(), nullable=True) + + numflag = sa.Column(sa.Integer(), nullable=True) + + subdepartment_number = sa.Column('subdept', sa.SmallInteger(), nullable=True) + + deposit = sa.Column(sa.Float(), nullable=True) + + local = sa.Column(sa.Integer(), nullable=True, default=0) + + store_id = sa.Column(sa.SmallInteger(), nullable=True) + + default_vendor_id = sa.Column(sa.Integer(), nullable=True) + + current_origin_id = sa.Column(sa.Integer(), nullable=True) + + auto_par = sa.Column(sa.Float(), nullable=True, default=0) + + price_rule_id = sa.Column(sa.Integer(), nullable=True, default=0) + + last_sold = sa.Column(sa.DateTime(), nullable=True) diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py index 3074dd1..456b1b8 100644 --- a/corepos/db/lane_op/model.py +++ b/corepos/db/lane_op/model.py @@ -88,141 +88,11 @@ class Department(Base): return self.name or "" -class Product(Base): +class Product(common.ProductBase, Base): """ - Represents a product, purchased and/or sold by the organization. + Data model for ``products`` table. """ __tablename__ = 'products' - # __table_args__ = ( - # sa.ForeignKeyConstraint(['department'], ['departments.dept_no']), - # sa.ForeignKeyConstraint(['subdept'], ['subdepts.subdept_no']), - # sa.ForeignKeyConstraint(['tax'], ['taxrates.id']), - # ) - - id = sa.Column(sa.Integer(), nullable=False, - primary_key=True, autoincrement=True) - - upc = sa.Column(sa.String(length=13), nullable=True) - - description = sa.Column(sa.String(length=30), nullable=True) - - brand = sa.Column(sa.String(length=30), nullable=True) - - formatted_name = sa.Column(sa.String(length=30), nullable=True) - - normal_price = sa.Column(sa.Float(), nullable=True) - - price_method = sa.Column('pricemethod', sa.SmallInteger(), nullable=True) - - group_price = sa.Column('groupprice', sa.Float(), nullable=True) - - quantity = sa.Column(sa.SmallInteger(), nullable=True) - - special_price = sa.Column(sa.Float(), nullable=True) - - special_price_method = sa.Column('specialpricemethod', sa.SmallInteger(), nullable=True) - - special_group_price = sa.Column('specialgroupprice', sa.Float(), nullable=True) - - special_quantity = sa.Column('specialquantity', sa.SmallInteger(), nullable=True) - - special_limit = sa.Column(sa.SmallInteger(), nullable=True) - - start_date = sa.Column(sa.DateTime(), nullable=True) - - end_date = sa.Column(sa.DateTime(), nullable=True) - - department_number = sa.Column('department', sa.SmallInteger(), nullable=True) - # department = orm.relationship( - # Department, - # primaryjoin=Department.number == department_number, - # foreign_keys=[department_number], - # doc=""" - # Reference to the :class:`Department` to which the product belongs. - # """) - - size = sa.Column(sa.String(length=9), nullable=True) - - tax_rate_id = sa.Column('tax', sa.SmallInteger(), nullable=True) - # tax_rate = orm.relationship(TaxRate) - - foodstamp = sa.Column(sa.Boolean(), nullable=True) - - scale = sa.Column(sa.Boolean(), nullable=True) - - scale_price = sa.Column('scaleprice', sa.Float(), nullable=True) - - mix_match_code = sa.Column('mixmatchcode', sa.String(length=13), nullable=True) - - created = sa.Column(sa.DateTime(), nullable=True) - - modified = sa.Column(sa.DateTime(), nullable=True) - - # 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 = sa.Column('tareweight', sa.Float(), nullable=True) - - discount = sa.Column(sa.SmallInteger(), nullable=True) - - discount_type = sa.Column('discounttype', sa.SmallInteger(), nullable=True) - - line_item_discountable = sa.Column(sa.Boolean(), nullable=True) - - unit_of_measure = sa.Column('unitofmeasure', sa.String(length=15), nullable=True) - - wicable = sa.Column(sa.SmallInteger(), nullable=True) - - quantity_enforced = sa.Column('qttyEnforced', sa.Boolean(), nullable=True) - - id_enforced = sa.Column('idEnforced', sa.SmallInteger(), nullable=True) - - cost = sa.Column(sa.Float(), nullable=True) - - special_cost = sa.Column(sa.Float(), nullable=True) - - received_cost = sa.Column(sa.Float(), nullable=True) - - in_use = sa.Column('inUse', sa.Boolean(), nullable=True) - - flags = sa.Column('numflag', sa.Integer(), nullable=True) - - subdepartment_number = sa.Column('subdept', sa.SmallInteger(), nullable=True) - # subdepartment = orm.relationship( - # Subdepartment, - # primaryjoin=Subdepartment.number == subdepartment_number, - # foreign_keys=[subdepartment_number], - # doc=""" - # Reference to the :class:`Subdepartment` to which the product belongs. - # """) - - deposit = sa.Column(sa.Float(), nullable=True) - - local = sa.Column(sa.Integer(), nullable=True, - default=0) # TODO: do we want a default here? - - store_id = sa.Column(sa.SmallInteger(), nullable=True) - - default_vendor_id = sa.Column(sa.Integer(), nullable=True) - # default_vendor = orm.relationship( - # Vendor, - # primaryjoin=Vendor.id == default_vendor_id, - # foreign_keys=[default_vendor_id], - # doc=""" - # Reference to the default :class:`Vendor` from which the product is obtained. - # """) - - current_origin_id = sa.Column(sa.Integer(), nullable=True) - - auto_par = sa.Column(sa.Float(), nullable=True, - default=0) # TODO: do we want a default here? - - price_rule_id = sa.Column(sa.Integer(), nullable=True) - - # TODO: some older DB's might not have this? guess we'll see - last_sold = sa.Column(sa.DateTime(), nullable=True) class CustomerClassic(Base): diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index ad4b89b..e374d37 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -536,7 +536,7 @@ class Origin(Base): return self.name or self.short_name or "" -class Product(Base): +class Product(common.ProductBase, Base): """ Represents a product, purchased and/or sold by the organization. """ @@ -547,120 +547,37 @@ class Product(Base): sa.ForeignKeyConstraint(['tax'], ['taxrates.id']), ) - id = sa.Column(sa.Integer(), primary_key=True, autoincrement=True, nullable=False) - - upc = sa.Column(sa.String(length=13), nullable=True) - - description = sa.Column(sa.String(length=30), nullable=True) - - brand = sa.Column(sa.String(length=30), nullable=True) - - formatted_name = sa.Column(sa.String(length=30), nullable=True) - - normal_price = sa.Column(sa.Float(), nullable=True) - - price_method = sa.Column('pricemethod', sa.SmallInteger(), nullable=True) - - group_price = sa.Column('groupprice', sa.Float(), nullable=True) - - quantity = sa.Column(sa.SmallInteger(), nullable=True) - - special_price = sa.Column(sa.Float(), nullable=True) - - special_price_method = sa.Column('specialpricemethod', sa.SmallInteger(), nullable=True) - - special_group_price = sa.Column('specialgroupprice', sa.Float(), nullable=True) - - special_quantity = sa.Column('specialquantity', sa.SmallInteger(), nullable=True) - - start_date = sa.Column(sa.DateTime(), nullable=True) - - end_date = sa.Column(sa.DateTime(), nullable=True) - - department_number = sa.Column('department', sa.SmallInteger(), nullable=True) department = orm.relationship( Department, - primaryjoin=Department.number == department_number, - foreign_keys=[department_number], + primaryjoin='Department.number == Product.department_number', + foreign_keys='Product.department_number', doc=""" Reference to the :class:`Department` to which the product belongs. """) - size = sa.Column(sa.String(length=9), nullable=True) - - tax_rate_id = sa.Column('tax', sa.SmallInteger(), nullable=True) tax_rate = orm.relationship(TaxRate) - foodstamp = sa.Column(sa.Boolean(), nullable=True) - - scale = sa.Column(sa.Boolean(), nullable=True) - - # TODO: yikes, did i just code this all wrong the first time? pretty sure - # this needs to change to a decimal column... - scale_price = sa.Column('scaleprice', sa.Boolean(), nullable=True) - # scale_price = sa.Column('scaleprice', sa.Numeric(precision=10, scale=2), nullable=True) - - mix_match_code = sa.Column('mixmatchcode', sa.String(length=13), nullable=True) - - created = sa.Column(StringableDateTime(), nullable=True) - - modified = sa.Column(sa.DateTime(), nullable=True) - # advertised = sa.Column(sa.Boolean(), nullable=True) - tare_weight = sa.Column('tareweight', sa.Float(), nullable=True) - - discount = sa.Column(sa.SmallInteger(), nullable=True) - - discount_type = sa.Column('discounttype', sa.SmallInteger(), nullable=True) - - line_item_discountable = sa.Column(sa.Boolean(), nullable=True) - - unit_of_measure = sa.Column('unitofmeasure', sa.String(length=15), nullable=True) - - wicable = sa.Column(sa.SmallInteger(), nullable=True) - - quantity_enforced = sa.Column('qttyEnforced', sa.Boolean(), nullable=True) - - id_enforced = sa.Column('idEnforced', sa.SmallInteger(), nullable=True) - - cost = sa.Column(sa.Float(), nullable=True) - - in_use = sa.Column('inUse', sa.Boolean(), nullable=True) - - flags = sa.Column('numflag', sa.Integer(), nullable=True) - - subdepartment_number = sa.Column('subdept', sa.SmallInteger(), nullable=True) subdepartment = orm.relationship( Subdepartment, - primaryjoin=Subdepartment.number == subdepartment_number, - foreign_keys=[subdepartment_number], + primaryjoin='Subdepartment.number == Product.subdepartment_number', + foreign_keys='Product.subdepartment_number', doc=""" Reference to the :class:`Subdepartment` to which the product belongs. """) - deposit = sa.Column(sa.Float(), nullable=True) - - local = sa.Column(sa.Integer(), nullable=True) - - store_id = sa.Column(sa.SmallInteger(), nullable=True) - - default_vendor_id = sa.Column(sa.Integer(), nullable=True) default_vendor = orm.relationship( Vendor, - primaryjoin=Vendor.id == default_vendor_id, - foreign_keys=[default_vendor_id], + primaryjoin='Vendor.id == Product.default_vendor_id', + foreign_keys='Product.default_vendor_id', doc=""" Reference to the default :class:`Vendor` from which the product is obtained. """) + # TODO: deprecate / remove this? vendor = orm.synonym('default_vendor') - current_origin_id = sa.Column(sa.Integer(), nullable=True) - - # TODO: some older DB's might not have this? guess we'll see - last_sold = sa.Column(sa.DateTime(), nullable=True) - like_code = association_proxy( '_like_code', 'like_code', creator=lambda lc: ProductLikeCode(like_code=lc), From 97a1396a543e052caf9bd24c5ad1837f9e68eb39 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 25 Jan 2025 17:01:10 -0600 Subject: [PATCH 69/72] feat: use true column names for transaction data models as much as i kind of want to "rename" some of these for convenience, it seems safest here to just stick with true names to avoid confusion --- corepos/db/common/trans.py | 59 ++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/corepos/db/common/trans.py b/corepos/db/common/trans.py index 2b4e503..9ec5601 100644 --- a/corepos/db/common/trans.py +++ b/corepos/db/common/trans.py @@ -36,25 +36,21 @@ class TransactionDetailBase: """ # register - register_number = sa.Column('register_no', sa.Integer(), nullable=True) + register_no = sa.Column(sa.Integer(), nullable=True) # txn - transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True) - transaction_number = sa.Column('trans_no', sa.Integer(), nullable=True) - transaction_type = sa.Column('trans_type', sa.String(length=1), nullable=True) - transaction_subtype = sa.Column('trans_subtype', sa.String(length=2), nullable=True) + trans_id = sa.Column(sa.Integer(), nullable=True) + trans_no = sa.Column(sa.Integer(), nullable=True) + trans_type = sa.Column(sa.String(length=1), nullable=True) + trans_subtype = sa.Column(sa.String(length=2), nullable=True) trans_status = sa.Column(sa.String(length=1), nullable=True) - @declared_attr - def transaction_status(self): - return orm.synonym('trans_status') - # cashier - employee_number = sa.Column('emp_no', sa.Integer(), nullable=True) + emp_no = sa.Column(sa.Integer(), nullable=True) # customer - card_number = sa.Column('card_no', sa.Integer(), nullable=True) - member_type = sa.Column('memType', sa.Integer(), nullable=True) + card_no = sa.Column(sa.Integer(), nullable=True) + memType = sa.Column(sa.Integer(), nullable=True) staff = sa.Column(sa.Boolean(), nullable=True) ############################## @@ -63,7 +59,7 @@ class TransactionDetailBase: upc = sa.Column(sa.String(length=13), nullable=True) - department_number = sa.Column('department', sa.Integer(), nullable=True) + department = sa.Column(sa.Integer(), nullable=True) description = sa.Column(sa.String(length=30), nullable=True) @@ -73,51 +69,46 @@ class TransactionDetailBase: cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - unitPrice = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True) - - @declared_attr - def unit_price(self): - return orm.synonym('unitPrice') + unitPrice = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) total = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - reg_price = sa.Column('regPrice', sa.Numeric(precision=10, scale=2), nullable=True) + regPrice = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) tax = sa.Column(sa.SmallInteger(), nullable=True) - @declared_attr - def tax_rate_id(self): - return orm.synonym('tax') - - food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True) + foodstamp = sa.Column(sa.Boolean(), nullable=True) discount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - member_discount = sa.Column('memDiscount', sa.Numeric(precision=10, scale=2), nullable=True) + memDiscount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) discountable = sa.Column(sa.Boolean(), nullable=True) - discount_type = sa.Column('discounttype', sa.Integer(), nullable=True) + discounttype = sa.Column(sa.Integer(), nullable=True) voided = sa.Column(sa.Integer(), nullable=True) - percent_discount = sa.Column('percentDiscount', sa.Integer(), nullable=True) + percentDiscount = sa.Column(sa.Integer(), nullable=True) - item_quantity = sa.Column('ItemQtty', sa.Float(), nullable=True) + ItemQtty = sa.Column(sa.Float(), nullable=True) - volume_discount_type = sa.Column('volDiscType', sa.Integer(), nullable=True) + volDiscType = sa.Column(sa.Integer(), nullable=True) volume = sa.Column(sa.Integer(), nullable=True) - volume_special = sa.Column('VolSpecial', sa.Numeric(precision=10, scale=2), nullable=True) + VolSpecial = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True) - mix_match = sa.Column('mixMatch', sa.String(length=13), nullable=True) + mixMatch = sa.Column(sa.String(length=13), nullable=True) matched = sa.Column(sa.Boolean(), nullable=True) - num_flag = sa.Column('numflag', sa.Integer(), nullable=True, default=0) + numflag = sa.Column(sa.Integer(), nullable=True, default=0) - char_flag = sa.Column('charflag', sa.String(length=2), nullable=True) + charflag = sa.Column(sa.String(length=2), nullable=True) def __str__(self): - return self.description or '' + txnid = '-'.join([str(val) for val in [self.register_no, + self.trans_no, + self.trans_id]]) + return f"{txnid} {self.description or ''}" From 8359e5692e62e5c289f90871b48be160d141c8c3 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 1 Feb 2025 15:19:12 -0600 Subject: [PATCH 70/72] =?UTF-8?q?bump:=20version=200.4.0=20=E2=86=92=200.5?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 12 ++++++++++++ pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca4e4bb..0c46d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.5.0 (2025-02-01) + +### Feat + +- use true column names for transaction data models + +### Fix + +- define common base schema for Product model +- add `Parameter` model for lane_op +- add model for lane_trans `LocalTrans` + ## v0.4.0 (2025-01-24) ### Feat diff --git a/pyproject.toml b/pyproject.toml index 75ef20f..35eb3a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.4.0" +version = "0.5.0" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] From 9b9260ba4b9428da5f2ced0d542e1307c88a90ec Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 20 Feb 2025 09:30:24 -0600 Subject: [PATCH 71/72] fix: add `Product.default_vendor_item` convenience property --- corepos/db/office_op/model.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py index e374d37..60ad478 100644 --- a/corepos/db/office_op/model.py +++ b/corepos/db/office_op/model.py @@ -583,6 +583,32 @@ class Product(common.ProductBase, Base): creator=lambda lc: ProductLikeCode(like_code=lc), ) + vendor_items = orm.relationship( + 'VendorItem', + back_populates='product', + primaryjoin='VendorItem.upc == Product.upc', + foreign_keys='VendorItem.upc', + order_by='VendorItem.vendor_item_id', + doc=""" + List of :class:`VendorItem` records for this product. + """) + + @property + def default_vendor_item(self): + """ + Returns the "default" vendor item record. This will + correspond to the :attr:`default_vendor` if possible. + + :rtype: :class:`VendorItem` or ``None`` + """ + if self.default_vendor: + for item in self.vendor_items: + if item.vendor_id == self.default_vendor.id: + return item + + if self.vendor_items: + return self.vendor_items[0] + @property def full_description(self): fields = ['brand', 'description', 'size'] @@ -732,17 +758,12 @@ class VendorItem(Base): upc = sa.Column(sa.String(length=13), nullable=False) product = orm.relationship( Product, + back_populates='vendor_items', primaryjoin=Product.upc == upc, foreign_keys=[upc], doc=""" Reference to the :class:`Product` to which this record applies. - """, - backref=orm.backref( - 'vendor_items', - order_by=vendor_item_id, - doc=""" - List of :class:`VendorItem` records for this product. - """)) + """) brand = sa.Column(sa.String(length=50), nullable=True) From feb2d3471e3451a674ca77c26fac6dba0e371f28 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 20 Feb 2025 09:31:29 -0600 Subject: [PATCH 72/72] =?UTF-8?q?bump:=20version=200.5.0=20=E2=86=92=200.5?= =?UTF-8?q?.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c46d8c..2c26baa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to pyCOREPOS will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.5.1 (2025-02-20) + +### Fix + +- add `Product.default_vendor_item` convenience property + ## v0.5.0 (2025-02-01) ### Feat diff --git a/pyproject.toml b/pyproject.toml index 35eb3a8..cf50f41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "pyCOREPOS" -version = "0.5.0" +version = "0.5.1" description = "Python Interface to CORE POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]