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')