diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/.keepme b/docs/_static/.keepme new file mode 100644 index 0000000..e69de29 diff --git a/docs/api/wutta_corepos.app.rst b/docs/api/wutta_corepos.app.rst new file mode 100644 index 0000000..6f51bb9 --- /dev/null +++ b/docs/api/wutta_corepos.app.rst @@ -0,0 +1,6 @@ + +``wutta_corepos.app`` +===================== + +.. automodule:: wutta_corepos.app + :members: diff --git a/docs/api/wutta_corepos.conf.rst b/docs/api/wutta_corepos.conf.rst new file mode 100644 index 0000000..58c5086 --- /dev/null +++ b/docs/api/wutta_corepos.conf.rst @@ -0,0 +1,6 @@ + +``wutta_corepos.conf`` +====================== + +.. automodule:: wutta_corepos.conf + :members: diff --git a/docs/api/wutta_corepos.handler.rst b/docs/api/wutta_corepos.handler.rst new file mode 100644 index 0000000..7954a8f --- /dev/null +++ b/docs/api/wutta_corepos.handler.rst @@ -0,0 +1,6 @@ + +``wutta_corepos.handler`` +========================= + +.. automodule:: wutta_corepos.handler + :members: diff --git a/docs/api/wutta_corepos.rst b/docs/api/wutta_corepos.rst new file mode 100644 index 0000000..1b6f3c0 --- /dev/null +++ b/docs/api/wutta_corepos.rst @@ -0,0 +1,6 @@ + +``wutta_corepos`` +================= + +.. automodule:: wutta_corepos + :members: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..301c604 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,38 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +from importlib.metadata import version as get_version + +project = 'Wutta-COREPOS' +copyright = '2025, Lance Edgar' +author = 'Lance Edgar' +release = get_version('Wutta-COREPOS') + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', + 'sphinx.ext.todo', +] + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +intersphinx_mapping = { + 'wuttjamaican': ('https://rattailproject.org/docs/wuttjamaican/', None), +} + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'furo' +html_static_path = ['_static'] diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..44e43b7 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,28 @@ + +Wutta-COREPOS +============= + +This package adds basic integration with `CORE-POS`_, using +`pyCOREPOS`_. + +Its main purpose is to setup DB connections for CORE Office. + +.. _CORE-POS: https://www.core-pos.com/ + +.. _pyCOREPOS: https://pypi.org/project/pyCOREPOS/ + + +.. toctree:: + :maxdepth: 2 + :caption: Documentation + + narr/install + +.. toctree:: + :maxdepth: 1 + :caption: API + + api/wutta_corepos + api/wutta_corepos.app + api/wutta_corepos.conf + api/wutta_corepos.handler diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/narr/install.rst b/docs/narr/install.rst new file mode 100644 index 0000000..33e7cd2 --- /dev/null +++ b/docs/narr/install.rst @@ -0,0 +1,32 @@ + +Installation +============ + +This assumes you already have a :doc:`WuttJamaican app +` setup and working. + +Install the Wutta-COREPOS package to your virtual environment: + +.. code-block:: sh + + 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. + +.. code-block:: ini + + [corepos] + office.url = http://localhost/fannie/ + + [corepos.db.office_op] + default.url = mysql+mysqlconnector://localhost/core_op + + [corepos.db.office_trans] + default.url = mysql+mysqlconnector://localhost/core_trans + + [corepos.db.office_arch] + default.url = mysql+mysqlconnector://localhost/trans_archive + +And that's it, the CORE-POS integration is configured. diff --git a/src/wutta_corepos/app.py b/src/wutta_corepos/app.py index f143788..a1a8724 100644 --- a/src/wutta_corepos/app.py +++ b/src/wutta_corepos/app.py @@ -36,6 +36,11 @@ class WuttaCoreposAppProvider(AppProvider): """ def get_corepos_handler(self, **kwargs): + """ + Get the configured CORE-POS integration handler. + + :rtype: :class:`~wutta_corepos.handler.CoreposHandler` + """ if not hasattr(self, 'corepos_handler'): spec = self.config.get(f'{self.appname}.corepos_handler', default='wutta_corepos.handler:CoreposHandler') diff --git a/src/wutta_corepos/conf.py b/src/wutta_corepos/conf.py index 66266d2..13ece08 100644 --- a/src/wutta_corepos/conf.py +++ b/src/wutta_corepos/conf.py @@ -40,7 +40,7 @@ class WuttaCoreposConfigExtension(WuttaConfigExtension): * ``office_trans`` (default name ``core_trans``) * ``office_arch`` (default name ``trans_archive``) - The config object will be given the following attributes: + The :term:`config object` will be given the following attributes: .. data:: core_office_op_engine diff --git a/src/wutta_corepos/handler.py b/src/wutta_corepos/handler.py index a8fc8a2..f2afdbe 100644 --- a/src/wutta_corepos/handler.py +++ b/src/wutta_corepos/handler.py @@ -38,6 +38,11 @@ class CoreposHandler(GenericHandler): Returns the base URL for the CORE Office web app. Note that the return value is stripped of final slash. + + :param require: If true, an error is raised when URL cannot be + determined. + + :returns: URL as string. """ url = self.config.get('corepos.office.url', require=require) if url: @@ -45,40 +50,64 @@ class CoreposHandler(GenericHandler): def get_office_department_url( self, - number, + dept_id, office_url=None, - require=False, - **kwargs): + require=False): """ Returns the CORE Office URL for a Department. + + :param dept_id: Department 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}/item/departments/DepartmentEditor.php?did={number}' + return f'{office_url}/item/departments/DepartmentEditor.php?did={dept_id}' def get_office_likecode_url( self, - id, + likecode_id, office_url=None, - require=False, - **kwargs): + require=False): """ Returns the CORE Office URL for a Like Code. + + :param likecode_id: Like Code 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}/item/likecodes/LikeCodeEditor.php?start={id}' + return f'{office_url}/item/likecodes/LikeCodeEditor.php?start={likecode_id}' def get_office_product_url( self, upc, office_url=None, - require=False, - **kwargs): + require=False): """ Returns the CORE Office URL for a Product. + + :param upc: UPC 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) @@ -87,14 +116,22 @@ class CoreposHandler(GenericHandler): def get_office_vendor_url( self, - id, + vend_id, office_url=None, - require=False, - **kwargs): + require=False): """ Returns the CORE Office URL for a Vendor. + + :param vend_id: Vendor 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}/item/vendors/VendorIndexPage.php?vid={id}' + return f'{office_url}/item/vendors/VendorIndexPage.php?vid={vend_id}' diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..d2f7109 --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8; -*- + +from wuttjamaican.testing import ConfigTestCase + +from wutta_corepos import app as mod +from wutta_corepos.handler import CoreposHandler + + +class TestWuttaCoreposAppProvider(ConfigTestCase): + + def make_provider(self): + return mod.WuttaCoreposAppProvider(self.config) + + def test_get_report_handler(self): + handler = self.app.get_corepos_handler() + self.assertIsInstance(handler, CoreposHandler) diff --git a/tests/test_conf.py b/tests/test_conf.py new file mode 100644 index 0000000..c957dce --- /dev/null +++ b/tests/test_conf.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8; -*- + +from unittest import TestCase + +from wuttjamaican.conf import WuttaConfig + +from wutta_corepos import conf as mod + + +class TestWuttaCoreposConfigExtension(TestCase): + + def test_configure(self): + config = WuttaConfig() + + # no engines by default + self.assertFalse(hasattr(config, 'core_office_op_engine')) + self.assertFalse(hasattr(config, 'core_office_trans_engine')) + self.assertFalse(hasattr(config, 'core_office_arch_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) + + # but config can change that + config.setdefault('corepos.db.office_op.default.url', 'sqlite://') + ext.configure(config) + self.assertIsNotNone(config.core_office_op_engine) + self.assertEqual(str(config.core_office_op_engine.url), 'sqlite://') diff --git a/tests/test_handler.py b/tests/test_handler.py new file mode 100644 index 0000000..e29835b --- /dev/null +++ b/tests/test_handler.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8; -*- + +from wuttjamaican.testing import ConfigTestCase +from wuttjamaican.exc import ConfigurationError + +from wutta_corepos import handler as mod + + +class TestCoreposHandler(ConfigTestCase): + + def make_handler(self): + return mod.CoreposHandler(self.config) + + def test_get_office_url(self): + handler = self.make_handler() + + # null by default + self.assertIsNone(handler.get_office_url()) + + # error if required + self.assertRaises(ConfigurationError, handler.get_office_url, require=True) + + # config can specify (traliing slash is stripped) + self.config.setdefault('corepos.office.url', 'http://localhost/fannie/') + self.assertEqual(handler.get_office_url(), 'http://localhost/fannie') + self.assertEqual(handler.get_office_url(require=True), 'http://localhost/fannie') + + def test_get_office_department_url(self): + handler = self.make_handler() + + # null + self.assertIsNone(handler.get_office_department_url(7)) + + # typical + 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_likecode_url(self): + handler = self.make_handler() + + # null + self.assertIsNone(handler.get_office_likecode_url(7)) + + # typical + self.config.setdefault('corepos.office.url', 'http://localhost/fannie/') + self.assertEqual(handler.get_office_likecode_url(7), 'http://localhost/fannie/item/likecodes/LikeCodeEditor.php?start=7') + + def test_get_office_product_url(self): + handler = self.make_handler() + + # null + self.assertIsNone(handler.get_office_product_url('07430500132')) + + # typical + self.config.setdefault('corepos.office.url', 'http://localhost/fannie/') + self.assertEqual(handler.get_office_product_url('07430500132'), 'http://localhost/fannie/item/ItemEditorPage.php?searchupc=07430500132') + + def test_get_office_vendor_url(self): + handler = self.make_handler() + + # null + self.assertIsNone(handler.get_office_vendor_url(7)) + + # typical + self.config.setdefault('corepos.office.url', 'http://localhost/fannie/') + self.assertEqual(handler.get_office_vendor_url(7), 'http://localhost/fannie/item/vendors/VendorIndexPage.php?vid=7')