diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b25e7c..7feebeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,27 +5,6 @@ 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 - -- add latest schema columns on app startup, unless not supported - ## v0.2.0 (2025-01-13) ### Feat diff --git a/docs/api/wutta_corepos.db.model.rst b/docs/api/wutta_corepos.db.model.rst deleted file mode 100644 index 9954c44..0000000 --- a/docs/api/wutta_corepos.db.model.rst +++ /dev/null @@ -1,6 +0,0 @@ - -``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 deleted file mode 100644 index c04bb3e..0000000 --- a/docs/api/wutta_corepos.db.rst +++ /dev/null @@ -1,6 +0,0 @@ - -``wutta_corepos.db`` -==================== - -.. automodule:: wutta_corepos.db - :members: diff --git a/docs/conf.py b/docs/conf.py index 2f4f53f..4759410 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://docs.wuttaproject.org/wuttaweb/', None), - 'wuttjamaican': ('https://docs.wuttaproject.org/wuttjamaican/', None), + 'wuttaweb': ('https://rattailproject.org/docs/wuttaweb/', None), + 'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None), } diff --git a/docs/index.rst b/docs/index.rst index a9beddd..4f5d57b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,15 +5,8 @@ Wutta-COREPOS This package adds basic integration with `CORE-POS`_, using `pyCOREPOS`_. -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 +Its main purpose is to setup DB connections for CORE Office, but it +also contains basic readonly web views for some CORE tables. .. _CORE-POS: https://www.core-pos.com/ @@ -33,8 +26,6 @@ It provides the following: 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 fdff505..33e7cd2 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -12,7 +12,8 @@ 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. +related settings. Note that so far, only CORE Office DB connections +are supported. .. code-block:: ini @@ -28,58 +29,4 @@ related settings. [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. - - -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/pyproject.toml b/pyproject.toml index ba60abb..6f77e55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "Wutta-COREPOS" -version = "0.3.0" +version = "0.2.0" description = "Wutta Framework integration for CORE-POS" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] @@ -28,8 +28,8 @@ classifiers = [ ] requires-python = ">= 3.8" dependencies = [ - "pyCOREPOS>=0.5.1", - "WuttJamaican>=0.20.3", + "pyCOREPOS>=0.3.3", + "WuttJamaican>=0.20.1", ] diff --git a/src/wutta_corepos/conf.py b/src/wutta_corepos/conf.py index 7c3affb..ef4c917 100644 --- a/src/wutta_corepos/conf.py +++ b/src/wutta_corepos/conf.py @@ -74,34 +74,6 @@ 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' @@ -128,23 +100,3 @@ 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) - - # 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): - from corepos.db.office_op.model import use_latest_columns - use_latest_columns() diff --git a/src/wutta_corepos/db/__init__.py b/src/wutta_corepos/db/__init__.py deleted file mode 100644 index e69de29..0000000 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 deleted file mode 100644 index 92ed240..0000000 --- a/src/wutta_corepos/db/alembic/versions/0f94089f1af1_initial_user_extension.py +++ /dev/null @@ -1,37 +0,0 @@ -"""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 deleted file mode 100644 index 89cf90d..0000000 --- a/src/wutta_corepos/db/model.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- 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/src/wutta_corepos/handler.py b/src/wutta_corepos/handler.py index f52ef8e..8b39795 100644 --- a/src/wutta_corepos/handler.py +++ b/src/wutta_corepos/handler.py @@ -60,24 +60,6 @@ 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. @@ -117,32 +99,6 @@ 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. @@ -180,28 +136,6 @@ 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/src/wutta_corepos/web/db.py b/src/wutta_corepos/web/db.py index 4c9e292..7faeff2 100644 --- a/src/wutta_corepos/web/db.py +++ b/src/wutta_corepos/web/db.py @@ -49,22 +49,6 @@ 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 @@ -80,15 +64,7 @@ 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/src/wutta_corepos/web/views/corepos/members.py b/src/wutta_corepos/web/views/corepos/members.py index 19a6268..66c967e 100644 --- a/src/wutta_corepos/web/views/corepos/members.py +++ b/src/wutta_corepos/web/views/corepos/members.py @@ -100,12 +100,6 @@ 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] diff --git a/tests/db/test_model.py b/tests/db/test_model.py deleted file mode 100644 index 19135d3..0000000 --- a/tests/db/test_model.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- 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') diff --git a/tests/test_conf.py b/tests/test_conf.py index 10f97a0..c957dce 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -16,21 +16,14 @@ 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 f751f22..6dc5077 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -34,18 +34,6 @@ 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://') @@ -73,24 +61,6 @@ 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() @@ -115,16 +85,6 @@ 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() diff --git a/tests/web/views/corepos/test_members.py b/tests/web/views/corepos/test_members.py index b3da7b0..6055ef9 100644 --- a/tests/web/views/corepos/test_members.py +++ b/tests/web/views/corepos/test_members.py @@ -1,7 +1,5 @@ # -*- coding: utf-8; -*- -from unittest.mock import patch - from sqlalchemy import orm from corepos.db.office_op import model as op_model @@ -11,7 +9,7 @@ from wuttaweb.testing import WebTestCase from wutta_corepos.web.views.corepos import members as mod -class TestMemberView(WebTestCase): +class TestProductView(WebTestCase): def make_view(self): return mod.MemberView(self.request) @@ -30,11 +28,8 @@ class TestMemberView(WebTestCase): view = self.make_view() grid = view.make_grid(model_class=view.model_class) self.assertNotIn('first_name', grid.renderers) - self.assertNotIn('first_name', grid.linked_columns) - with patch.object(self.request, 'is_root', new=True): - view.configure_grid(grid) + 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() diff --git a/tox.ini b/tox.ini index 49b89cb..0ff0e30 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = py38, py39, py310, py311 [testenv] -extras = web,tests +extras = tests commands = pytest {posargs} [testenv:coverage] @@ -12,6 +12,6 @@ commands = pytest --cov=wutta_corepos --cov-report=html --cov-fail-under=100 [testenv:docs] basepython = python3.11 -extras = web,docs +extras = docs changedir = docs commands = sphinx-build -b html -d {envtmpdir}/doctrees -W -T . {envtmpdir}/docs