From 93c5f579287cf7e173a88c0ec027fc83e4ddb239 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 13 Jan 2025 13:37:40 -0600 Subject: [PATCH 01/14] tests: need web extra when running tox tests --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0ff0e30..74597aa 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = py38, py39, py310, py311 [testenv] -extras = tests +extras = web,tests commands = pytest {posargs} [testenv:coverage] From 9946642bf2198479159124e52612963e8c0eb3f9 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 13 Jan 2025 13:41:14 -0600 Subject: [PATCH 02/14] docs: need web extra when building docs via tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 74597aa..49b89cb 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,6 @@ commands = pytest --cov=wutta_corepos --cov-report=html --cov-fail-under=100 [testenv:docs] basepython = python3.11 -extras = docs +extras = web,docs changedir = docs commands = sphinx-build -b html -d {envtmpdir}/doctrees -W -T . {envtmpdir}/docs From 9f779fe60fd6a1f5bb9165164cf49c7c5d899a9b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 11:01:09 -0600 Subject: [PATCH 03/14] fix: add latest schema columns on app startup, unless not supported this works around the issue where some CORE databases are too old (etc.) and are missing some schema columns. in such cases the config for "latest columns" should be disabled; otherwise we add them to schema on startup --- src/wutta_corepos/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wutta_corepos/conf.py b/src/wutta_corepos/conf.py index ef4c917..77bb6cc 100644 --- a/src/wutta_corepos/conf.py +++ b/src/wutta_corepos/conf.py @@ -100,3 +100,9 @@ class WuttaCoreposConfigExtension(WuttaConfigExtension): config.core_office_arch_engines = engines config.core_office_arch_engine = engines.get('default') Session.configure(bind=config.core_office_arch_engine) + + # define some schema columns "late" unless not supported + if config.get_bool('corepos.db.office_op.use_latest_columns', + default=True, usedb=False): + from corepos.db.office_op.model import use_latest_columns + use_latest_columns() From ce145ed00f162417215b3d5c8198fbfe36ef3458 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 11:02:28 -0600 Subject: [PATCH 04/14] =?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 | 6 ++++++ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7feebeb..24f220b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to Wutta-COREPOS 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 (2025-01-15) + +### Fix + +- add latest schema columns on app startup, unless not supported + ## v0.2.0 (2025-01-13) ### Feat diff --git a/pyproject.toml b/pyproject.toml index 6f77e55..afb21c9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "Wutta-COREPOS" -version = "0.2.0" +version = "0.2.1" description = "Wutta Framework integration for CORE-POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] From 34e9528f4b8cd43247740a8c21adad0643864cb0 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 11:06:15 -0600 Subject: [PATCH 05/14] fix: bump min version for pycorepos dependency --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index afb21c9..62e0817 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers = [ ] requires-python = ">= 3.8" dependencies = [ - "pyCOREPOS>=0.3.3", + "pyCOREPOS>=0.3.5", "WuttJamaican>=0.20.1", ] From d1de2389a589d7338e366ac171264745474a5b18 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 14:57:45 -0600 Subject: [PATCH 06/14] fix: add grid links for Members --- src/wutta_corepos/web/views/corepos/members.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wutta_corepos/web/views/corepos/members.py b/src/wutta_corepos/web/views/corepos/members.py index 66c967e..19a6268 100644 --- a/src/wutta_corepos/web/views/corepos/members.py +++ b/src/wutta_corepos/web/views/corepos/members.py @@ -100,6 +100,12 @@ class MemberView(CoreOpMasterView): g.set_renderer('last_name', self.render_customer_attr) g.set_sorter('last_name', op_model.CustomerClassic.last_name) + # links + if self.has_perm('view'): + g.set_link('card_number') + g.set_link('first_name') + g.set_link('last_name') + def render_customer_attr(self, member, key, value): """ """ customer = member.customers[0] From bf6bf63e6888bc2235f368b6e835aac90af70ee3 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 15 Jan 2025 17:19:59 -0600 Subject: [PATCH 07/14] tests: fix coverage for members view --- tests/web/views/corepos/test_members.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/web/views/corepos/test_members.py b/tests/web/views/corepos/test_members.py index 6055ef9..b3da7b0 100644 --- a/tests/web/views/corepos/test_members.py +++ b/tests/web/views/corepos/test_members.py @@ -1,5 +1,7 @@ # -*- coding: utf-8; -*- +from unittest.mock import patch + from sqlalchemy import orm from corepos.db.office_op import model as op_model @@ -9,7 +11,7 @@ from wuttaweb.testing import WebTestCase from wutta_corepos.web.views.corepos import members as mod -class TestProductView(WebTestCase): +class TestMemberView(WebTestCase): def make_view(self): return mod.MemberView(self.request) @@ -28,8 +30,11 @@ class TestProductView(WebTestCase): view = self.make_view() grid = view.make_grid(model_class=view.model_class) self.assertNotIn('first_name', grid.renderers) - view.configure_grid(grid) + self.assertNotIn('first_name', grid.linked_columns) + with patch.object(self.request, 'is_root', new=True): + view.configure_grid(grid) self.assertIn('first_name', grid.renderers) + self.assertIn('first_name', grid.linked_columns) def test_render_customer_attr(self): view = self.make_view() From e6921c853338069c583202a8fa47c9178e7db9ff Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 19:33:34 -0600 Subject: [PATCH 08/14] feat: add support for `lane_op` and `lane_trans` DB sessions, models --- docs/narr/install.rst | 13 +++++++++-- src/wutta_corepos/conf.py | 42 ++++++++++++++++++++++++++++++++++ src/wutta_corepos/handler.py | 44 ++++++++++++++++++++++++++++++++++++ src/wutta_corepos/web/db.py | 24 ++++++++++++++++++++ tests/test_conf.py | 7 ++++++ tests/test_handler.py | 30 ++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 2 deletions(-) diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 33e7cd2..bceda1c 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -12,8 +12,7 @@ Install the Wutta-COREPOS package to your virtual environment: pip install Wutta-COREPOS Edit your :term:`config file` to add CORE-POS DB connection info, and -related settings. Note that so far, only CORE Office DB connections -are supported. +related settings. .. code-block:: ini @@ -29,4 +28,14 @@ are supported. [corepos.db.office_arch] default.url = mysql+mysqlconnector://localhost/trans_archive + [corepos.db.lane_op] + keys = 01, 02 + 01.url = mysql+mysqlconnector://lane01/opdata + 02.url = mysql+mysqlconnector://lane02/opdata + + [corepos.db.lane_trans] + keys = 01, 02 + 01.url = mysql+mysqlconnector://lane01/translog + 02.url = mysql+mysqlconnector://lane02/translog + And that's it, the CORE-POS integration is configured. diff --git a/src/wutta_corepos/conf.py b/src/wutta_corepos/conf.py index 77bb6cc..7c3affb 100644 --- a/src/wutta_corepos/conf.py +++ b/src/wutta_corepos/conf.py @@ -74,6 +74,34 @@ class WuttaCoreposConfigExtension(WuttaConfigExtension): Dict of ``office_arch`` DB engines. May be empty if no config is found; otherwise there should at least be a ``default`` key defined, corresonding to :data:`core_office_arch_engine`. + + .. data:: core_lane_op_engine + + Primary engine for the ``lane_op`` DB. May be null if no + "default" engine is configured - which is *typical* for a + multi-lane environment. See :data:`core_lane_op_engines` for + the full set. + + .. data:: core_lane_op_engines + + Dict of ``lane_op`` DB engines. May be empty if no config is + found; otherwise keys are typically like ``01`` and ``02`` etc. + If present, the ``default`` key will correspond to + :data:`core_lane_op_engine`. + + .. data:: core_lane_trans_engine + + Primary engine for the ``lane_trans`` DB. May be null if no + "default" engine is configured - which is *typical* for a + multi-lane environment. See :data:`core_lane_trans_engines` + for the full set. + + .. data:: core_lane_trans_engines + + Dict of ``lane_trans`` DB engines. May be empty if no config + is found; otherwise keys are typically like ``01`` and ``02`` + etc. If present, the ``default`` key will correspond to + :data:`core_lane_trans_engine`. """ key = 'wutta_corepos' @@ -101,6 +129,20 @@ class WuttaCoreposConfigExtension(WuttaConfigExtension): config.core_office_arch_engine = engines.get('default') Session.configure(bind=config.core_office_arch_engine) + # lane_op + from corepos.db.lane_op import Session + engines = get_engines(config, 'corepos.db.lane_op') + config.core_lane_op_engines = engines + config.core_lane_op_engine = engines.get('default') + Session.configure(bind=config.core_lane_op_engine) + + # lane_trans + from corepos.db.lane_trans import Session + engines = get_engines(config, 'corepos.db.lane_trans') + config.core_lane_trans_engines = engines + config.core_lane_trans_engine = engines.get('default') + Session.configure(bind=config.core_lane_trans_engine) + # define some schema columns "late" unless not supported if config.get_bool('corepos.db.office_op.use_latest_columns', default=True, usedb=False): diff --git a/src/wutta_corepos/handler.py b/src/wutta_corepos/handler.py index 8b39795..6422f66 100644 --- a/src/wutta_corepos/handler.py +++ b/src/wutta_corepos/handler.py @@ -60,6 +60,24 @@ class CoreposHandler(GenericHandler): return model + def get_model_lane_op(self): + """ + Returns the :term:`data model` module for CORE Lane 'op' DB, + i.e. :mod:`pycorepos:corepos.db.lane_op.model`. + """ + from corepos.db.lane_op import model + + return model + + def get_model_lane_trans(self): + """ + Returns the :term:`data model` module for CORE Lane 'trans' + DB, i.e. :mod:`pycorepos:corepos.db.lane_trans.model`. + """ + from corepos.db.lane_trans import model + + return model + def make_session_office_op(self, dbkey='default', **kwargs): """ Make a new :term:`db session` for the CORE Office 'op' DB. @@ -99,6 +117,32 @@ class CoreposHandler(GenericHandler): kwargs['bind'] = self.config.core_office_arch_engines[dbkey] return Session(**kwargs) + def make_session_lane_op(self, dbkey='default', **kwargs): + """ + Make a new :term:`db session` for the CORE Lane 'op' DB. + + :returns: Instance of + :class:`pycorepos:corepos.db.lane_op.Session`. + """ + from corepos.db.lane_op import Session + + if 'bind' not in kwargs: + kwargs['bind'] = self.config.core_lane_op_engines[dbkey] + return Session(**kwargs) + + def make_session_lane_trans(self, dbkey='default', **kwargs): + """ + Make a new :term:`db session` for the CORE Lane 'trans' DB. + + :returns: Instance of + :class:`pycorepos:corepos.db.lane_trans.Session`. + """ + from corepos.db.lane_trans import Session + + if 'bind' not in kwargs: + kwargs['bind'] = self.config.core_lane_trans_engines[dbkey] + return Session(**kwargs) + def get_office_url(self, require=False): """ Returns the base URL for the CORE Office web app. diff --git a/src/wutta_corepos/web/db.py b/src/wutta_corepos/web/db.py index 7faeff2..4c9e292 100644 --- a/src/wutta_corepos/web/db.py +++ b/src/wutta_corepos/web/db.py @@ -49,6 +49,22 @@ in general. .. class:: ExtraCoreArchSessions Dict of secondary CORE Office 'arch' DB sessions, if applicable. + +.. class:: CoreLaneOpSession + + Primary web app :term:`db session` for CORE Lane 'op' DB. + +.. class:: CoreLaneTransSession + + Primary web app :term:`db session` for CORE Lane 'trans' DB. + +.. class:: ExtraCoreLaneOpSessions + + Dict of secondary CORE Lane 'op' DB sessions, if applicable. + +.. class:: ExtraCoreLaneTransSessions + + Dict of secondary CORE Lane 'trans' DB sessions, if applicable. """ from sqlalchemy.orm import sessionmaker, scoped_session @@ -64,7 +80,15 @@ register(CoreTransSession) CoreArchSession = scoped_session(sessionmaker()) register(CoreArchSession) +CoreLaneOpSession = scoped_session(sessionmaker()) +register(CoreLaneOpSession) + +CoreLaneTransSession = scoped_session(sessionmaker()) +register(CoreLaneTransSession) + # nb. these start out empty but may be populated on app startup ExtraCoreOpSessions = {} ExtraCoreTransSessions = {} ExtraCoreArchSessions = {} +ExtraCoreLaneOpSessions = {} +ExtraCoreLaneTransSessions = {} diff --git a/tests/test_conf.py b/tests/test_conf.py index c957dce..10f97a0 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -16,14 +16,21 @@ class TestWuttaCoreposConfigExtension(TestCase): self.assertFalse(hasattr(config, 'core_office_op_engine')) self.assertFalse(hasattr(config, 'core_office_trans_engine')) self.assertFalse(hasattr(config, 'core_office_arch_engine')) + self.assertFalse(hasattr(config, 'core_lane_op_engine')) + self.assertFalse(hasattr(config, 'core_lane_trans_engine')) ext = mod.WuttaCoreposConfigExtension() ext.configure(config) self.assertIsNone(config.core_office_op_engine) self.assertIsNone(config.core_office_trans_engine) self.assertIsNone(config.core_office_arch_engine) + self.assertIsNone(config.core_lane_op_engine) + self.assertIsNone(config.core_lane_trans_engine) # but config can change that config.setdefault('corepos.db.office_op.default.url', 'sqlite://') + config.setdefault('corepos.db.lane_trans.default.url', 'sqlite://') ext.configure(config) self.assertIsNotNone(config.core_office_op_engine) self.assertEqual(str(config.core_office_op_engine.url), 'sqlite://') + self.assertIsNotNone(config.core_lane_trans_engine) + self.assertEqual(str(config.core_lane_trans_engine.url), 'sqlite://') diff --git a/tests/test_handler.py b/tests/test_handler.py index 6dc5077..74f6f7b 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -34,6 +34,18 @@ class TestCoreposHandler(ConfigTestCase): arch_model = handler.get_model_office_arch() self.assertIs(arch_model, model) + def test_get_model_lane_op(self): + from corepos.db.lane_op import model + handler = self.make_handler() + op_model = handler.get_model_lane_op() + self.assertIs(op_model, model) + + def test_get_model_lane_trans(self): + from corepos.db.lane_trans import model + handler = self.make_handler() + trans_model = handler.get_model_lane_trans() + self.assertIs(trans_model, model) + def test_make_session_office_op(self): handler = self.make_handler() engine = sa.create_engine('sqlite://') @@ -61,6 +73,24 @@ class TestCoreposHandler(ConfigTestCase): self.assertIsInstance(arch_session, orm.Session) self.assertIs(arch_session.bind, engine) + def test_make_session_lane_op(self): + handler = self.make_handler() + engine = sa.create_engine('sqlite://') + with patch.object(self.config, 'core_lane_op_engines', create=True, + new={'default': engine}): + op_session = handler.make_session_lane_op() + self.assertIsInstance(op_session, orm.Session) + self.assertIs(op_session.bind, engine) + + def test_make_session_lane_trans(self): + handler = self.make_handler() + engine = sa.create_engine('sqlite://') + with patch.object(self.config, 'core_lane_trans_engines', create=True, + new={'default': engine}): + trans_session = handler.make_session_lane_trans() + self.assertIsInstance(trans_session, orm.Session) + self.assertIs(trans_session.bind, engine) + def test_get_office_url(self): handler = self.make_handler() From 1a9929c73401628039244e564790ed868deb4f93 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 19:35:14 -0600 Subject: [PATCH 09/14] fix: add `get_office_employee_url()` method for corepos handler --- src/wutta_corepos/handler.py | 22 ++++++++++++++++++++++ tests/test_handler.py | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/wutta_corepos/handler.py b/src/wutta_corepos/handler.py index 6422f66..f52ef8e 100644 --- a/src/wutta_corepos/handler.py +++ b/src/wutta_corepos/handler.py @@ -180,6 +180,28 @@ class CoreposHandler(GenericHandler): if office_url: return f'{office_url}/item/departments/DepartmentEditor.php?did={dept_id}' + def get_office_employee_url( + self, + employee_id, + office_url=None, + require=False): + """ + Returns the CORE Office URL for an Employee. + + :param employee_id: Employee ID for the URL. + + :param office_url: Root URL from :meth:`get_office_url()`. + + :param require: If true, an error is raised when URL cannot be + determined. + + :returns: URL as string. + """ + if not office_url: + office_url = self.get_office_url(require=require) + if office_url: + return f'{office_url}/admin/Cashiers/CashierEditor.php?emp_no={employee_id}' + def get_office_likecode_url( self, likecode_id, diff --git a/tests/test_handler.py b/tests/test_handler.py index 74f6f7b..f751f22 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -115,6 +115,16 @@ class TestCoreposHandler(ConfigTestCase): self.config.setdefault('corepos.office.url', 'http://localhost/fannie/') self.assertEqual(handler.get_office_department_url(7), 'http://localhost/fannie/item/departments/DepartmentEditor.php?did=7') + def test_get_office_employee_url(self): + handler = self.make_handler() + + # null + self.assertIsNone(handler.get_office_employee_url(7)) + + # typical + self.config.setdefault('corepos.office.url', 'http://localhost/fannie/') + self.assertEqual(handler.get_office_employee_url(7), 'http://localhost/fannie/admin/Cashiers/CashierEditor.php?emp_no=7') + def test_get_office_likecode_url(self): handler = self.make_handler() From 73192a162d5011ef5bb949f2134889134b7e5ee2 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 24 Jan 2025 19:59:46 -0600 Subject: [PATCH 10/14] fix: bump version requirement for pyCOREPOS --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 62e0817..76af27c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers = [ ] requires-python = ">= 3.8" dependencies = [ - "pyCOREPOS>=0.3.5", + "pyCOREPOS>=0.4.0", "WuttJamaican>=0.20.1", ] From 4ee5aa537280ecbb50d5b4821e4f9b27d2cb076b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 25 Jan 2025 17:20:55 -0600 Subject: [PATCH 11/14] feat: add app db schema extension, for CoreUser need a way to map Wutta User to CORE Employee for auth purposes --- docs/api/wutta_corepos.db.model.rst | 6 ++ docs/api/wutta_corepos.db.rst | 6 ++ docs/index.rst | 13 +++- docs/narr/install.rst | 44 ++++++++++++ src/wutta_corepos/db/__init__.py | 0 .../0f94089f1af1_initial_user_extension.py | 37 ++++++++++ src/wutta_corepos/db/model.py | 67 +++++++++++++++++++ tests/db/test_model.py | 16 +++++ 8 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 docs/api/wutta_corepos.db.model.rst create mode 100644 docs/api/wutta_corepos.db.rst create mode 100644 src/wutta_corepos/db/__init__.py create mode 100644 src/wutta_corepos/db/alembic/versions/0f94089f1af1_initial_user_extension.py create mode 100644 src/wutta_corepos/db/model.py create mode 100644 tests/db/test_model.py diff --git a/docs/api/wutta_corepos.db.model.rst b/docs/api/wutta_corepos.db.model.rst new file mode 100644 index 0000000..9954c44 --- /dev/null +++ b/docs/api/wutta_corepos.db.model.rst @@ -0,0 +1,6 @@ + +``wutta_corepos.db.model`` +========================== + +.. automodule:: wutta_corepos.db.model + :members: diff --git a/docs/api/wutta_corepos.db.rst b/docs/api/wutta_corepos.db.rst new file mode 100644 index 0000000..c04bb3e --- /dev/null +++ b/docs/api/wutta_corepos.db.rst @@ -0,0 +1,6 @@ + +``wutta_corepos.db`` +==================== + +.. automodule:: wutta_corepos.db + :members: diff --git a/docs/index.rst b/docs/index.rst index 4f5d57b..a9beddd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,8 +5,15 @@ Wutta-COREPOS This package adds basic integration with `CORE-POS`_, using `pyCOREPOS`_. -Its main purpose is to setup DB connections for CORE Office, but it -also contains basic readonly web views for some CORE tables. +It provides the following: + +* standard configuration for CORE Office + Lane databases +* special :term:`handler` for CORE integration + (:class:`~wutta_corepos.handler.CoreposHandler`) +* readonly web views for primary CORE Office DB tables +* :term:`data model` extension to map + :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` to CORE + Employee .. _CORE-POS: https://www.core-pos.com/ @@ -26,6 +33,8 @@ also contains basic readonly web views for some CORE tables. api/wutta_corepos api/wutta_corepos.app api/wutta_corepos.conf + api/wutta_corepos.db + api/wutta_corepos.db.model api/wutta_corepos.handler api/wutta_corepos.web api/wutta_corepos.web.db diff --git a/docs/narr/install.rst b/docs/narr/install.rst index bceda1c..fdff505 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -39,3 +39,47 @@ related settings. 02.url = mysql+mysqlconnector://lane02/translog And that's it, the CORE-POS integration is configured. + + +Schema Extension +---------------- + +As of writing the only reason to add the schema extension is if you +need to map Wutta Users to CORE Employees, for auth (login) purposes. +So this section can be skipped if you do not need that. + +This will effectively add the +:attr:`~wutta_corepos.db.model.CoreUser.corepos_employee_number` +attribute on the +:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` model. + +First you must override the :term:`app model` with your own. To do +this, create your own module (e.g. ``poser.db.model``) to contain:: + + from wuttjamaican.db.model import * + from wutta_corepos.db.model import * + +Then configure your app model to override the default: + +.. code-block:: ini + + [wutta] + model_spec = poser.db.model + +Then configure the Alembic section for schema migrations: + +.. code-block:: ini + + [alembic] + script_location = wuttjamaican.db:alembic + version_locations = wutta_corepos.db:alembic/versions wuttjamaican.db:alembic/versions + +And finally run the Alembic command to migrate: + +.. code-block:: sh + + cd /path/to/env + bin/alembic -c app/wutta.conf upgrade heads + +That should do it, from then on any changes will be migrated +automatically during upgrade. diff --git a/src/wutta_corepos/db/__init__.py b/src/wutta_corepos/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/wutta_corepos/db/alembic/versions/0f94089f1af1_initial_user_extension.py b/src/wutta_corepos/db/alembic/versions/0f94089f1af1_initial_user_extension.py new file mode 100644 index 0000000..92ed240 --- /dev/null +++ b/src/wutta_corepos/db/alembic/versions/0f94089f1af1_initial_user_extension.py @@ -0,0 +1,37 @@ +"""initial user extension + +Revision ID: 0f94089f1af1 +Revises: +Create Date: 2025-01-24 21:13:14.359200 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import wuttjamaican.db.util + + +# revision identifiers, used by Alembic. +revision: str = '0f94089f1af1' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = ('wutta_corepos',) +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + + # corepos_user + op.create_table('corepos_user', + sa.Column('uuid', wuttjamaican.db.util.UUID(), nullable=False), + sa.Column('corepos_employee_number', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['uuid'], ['user.uuid'], name=op.f('fk_corepos_user_uuid_user')), + sa.PrimaryKeyConstraint('uuid', name=op.f('pk_corepos_user')), + sa.UniqueConstraint('corepos_employee_number', name=op.f('uq_corepos_user_corepos_employee_number')) + ) + + +def downgrade() -> None: + + # corepos_user + op.drop_table('corepos_user') diff --git a/src/wutta_corepos/db/model.py b/src/wutta_corepos/db/model.py new file mode 100644 index 0000000..89cf90d --- /dev/null +++ b/src/wutta_corepos/db/model.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8; -*- +################################################################################ +# +# Wutta-COREPOS -- Wutta Framework integration for CORE-POS +# Copyright © 2025 Lance Edgar +# +# This file is part of Wutta Framework. +# +# Wutta Framework 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. +# +# Wutta Framework 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 +# Wutta Framework. If not, see . +# +################################################################################ +""" +Data models for CORE-POS integration +""" + +import sqlalchemy as sa +from sqlalchemy import orm + +from wuttjamaican.db import model + + +class CoreUser(model.Base): + """ + CORE-POS extension for + :class:`~wuttjamaican:wuttjamaican.db.model.auth.User`. + """ + + __tablename__ = 'corepos_user' + + uuid = model.uuid_column(sa.ForeignKey('user.uuid'), default=None) + user = orm.relationship( + model.User, + cascade_backrefs=False, + doc=""" + Reference to the + :class:`~wuttjamaican:wuttjamaican.db.model.auth.User` which + this record extends. + """, + backref=orm.backref( + '_corepos', + uselist=False, + cascade='all, delete-orphan', + cascade_backrefs=False, + doc=""" + Reference to the CORE-POS extension record for the user. + """) + ) + + corepos_employee_number = sa.Column(sa.Integer(), nullable=False, unique=True, doc=""" + ``employees.emp_no`` value for the user within CORE-POS. + """) + + def __str__(self): + return str(self.user) + +CoreUser.make_proxy(model.User, '_corepos', 'corepos_employee_number') diff --git a/tests/db/test_model.py b/tests/db/test_model.py new file mode 100644 index 0000000..19135d3 --- /dev/null +++ b/tests/db/test_model.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8; -*- + +from wuttjamaican.testing import ConfigTestCase +from wuttjamaican.db.model import User + +from wutta_corepos.db import model as mod + + +class TestCoreUser(ConfigTestCase): + + def test_str(self): + user = User(username='barney') + self.assertEqual(str(user), 'barney') + + ext = mod.CoreUser(user=user, corepos_employee_number=42) + self.assertEqual(str(ext), 'barney') From 4df38318e38182d6901df87584da180de0d0ffa9 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 25 Jan 2025 19:11:46 -0600 Subject: [PATCH 12/14] fix: bump min version for wuttjamaican need Base.make_proxy() --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 76af27c..ffd2ca7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ classifiers = [ requires-python = ">= 3.8" dependencies = [ "pyCOREPOS>=0.4.0", - "WuttJamaican>=0.20.1", + "WuttJamaican>=0.20.3", ] From f0ac1d9bd46c9d0359b6aa6f31e4fb822de5b8f0 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 18 Feb 2025 12:16:32 -0600 Subject: [PATCH 13/14] docs: update intersphinx doc links per server migration --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4759410..2f4f53f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,8 +27,8 @@ templates_path = ['_templates'] exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] intersphinx_mapping = { - 'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None), - 'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None), + 'wuttaweb': ('https://docs.wuttaproject.org/wuttaweb/', None), + 'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None), } From eb9291fce7f4d5bd87053266b9e0d5444c0f7656 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 20 Feb 2025 09:32:26 -0600 Subject: [PATCH 14/14] =?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 | 15 +++++++++++++++ pyproject.toml | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f220b..9b25e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to Wutta-COREPOS 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 (2025-02-20) + +### Feat + +- add app db schema extension, for CoreUser +- add support for `lane_op` and `lane_trans` DB sessions, models + +### Fix + +- bump min version for wuttjamaican +- bump version requirement for pyCOREPOS +- add `get_office_employee_url()` method for corepos handler +- add grid links for Members +- bump min version for pycorepos dependency + ## v0.2.1 (2025-01-15) ### Fix diff --git a/pyproject.toml b/pyproject.toml index ffd2ca7..ba60abb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "Wutta-COREPOS" -version = "0.2.1" +version = "0.3.0" description = "Wutta Framework integration for CORE-POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] @@ -28,7 +28,7 @@ classifiers = [ ] requires-python = ">= 3.8" dependencies = [ - "pyCOREPOS>=0.4.0", + "pyCOREPOS>=0.5.1", "WuttJamaican>=0.20.3", ]