From 7225d6b4536c2bfa8e0454f3e6dc7fd85a6caee4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 3 Jan 2026 20:52:44 -0600 Subject: [PATCH 1/2] fix: add basic support for install/setup of terminal only if installing alongside existing server app; can improve later --- docs/narr/install.rst | 52 ++++++- pyproject.toml | 2 +- src/wuttapos/cli/run.py | 11 +- src/wuttapos/install.py | 135 ++++++++++++++++-- .../installer-templates/terminal.conf.mako | 17 +++ src/wuttapos/terminal/app.py | 8 +- 6 files changed, 206 insertions(+), 19 deletions(-) create mode 100644 src/wuttapos/installer-templates/terminal.conf.mako diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 7a3c767..8992b50 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -9,7 +9,7 @@ For now this only describes a *development* setup, which assumes the following: * Python 3.11 or newer * database in PostgreSQL or MySQL -These steps will setup both the server and terminal in a shared +These steps will setup both the Server and Terminal in a shared virtual environment. @@ -40,13 +40,13 @@ Install Server **Please note, you must create your database before running the installer.** -Run the WuttaPOS installer to setup the server app: +Run the WuttaPOS installer to setup the Server app: .. code-block:: ini bin/wuttapos install -Now you can run the server app via command line: +Now you can run the Server app via command line: .. code-block:: ini @@ -86,4 +86,48 @@ should be improved - but that will come later. Install Terminal ---------------- -TODO: this is not yet documented +**Please note, this assumes you already installed the Server as described above.** + +For now, we'll just install the Terminal alongside the Server. They +will share the same virtual environment, installed code, app database, +and "most of" the config files. + +So within your virtual environment, run the same installer again: + +.. code-block:: ini + + bin/wuttapos install + +.. note:: + + You will be asked for store and terminal IDs - these should be + valid and refer to actual records for those types, within the + database. See the Admin menu of the Server web app to manage those + records. + +At this point the Terminal app should be ready to go. To run the +standalone GUI: + +.. code-block:: sh + + bin/wuttapos -c app/terminal.conf run + +You will need to login; the POS uses a special set of permissions but +the first admin user should already have all that are needed. So just +login using same credentials as for the Server app. + +It's also possible (due to the magic of Flet/Flutter) to *serve* the +Terminal as a web app: + +.. code-block:: sh + + bin/wuttapos -c app/terminal.conf serve + +In that case (by default) you can browse it at http://localhost:8332 + +That might be useful for online demo purposes etc. but definitely the +intention is to run as standalone GUI for production. (However the +story around tablets is TBD.) + +For now, running as a web service like that is not a supported +feature. diff --git a/pyproject.toml b/pyproject.toml index f3f761a..3328535 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ classifiers = [ ] dependencies = [ "psycopg2", - "WuttJamaican[db]>=0.28.2", + "WuttJamaican[db]>=0.28.4", "WuttaSync", ] diff --git a/src/wuttapos/cli/run.py b/src/wuttapos/cli/run.py index da47109..1b097cf 100644 --- a/src/wuttapos/cli/run.py +++ b/src/wuttapos/cli/run.py @@ -24,6 +24,8 @@ See also: :ref:`wuttapos-run` """ +import os + import typer from wuttapos.cli import wuttapos_typer @@ -37,4 +39,11 @@ def run(ctx: typer.Context): from wuttapos.terminal.app import run_app config = ctx.parent.wutta_config - run_app(config) + + # nb. it does not seem possible (?) to inject our config when + # launching the Flet app, which means it will create a *separate* + # config for itself..this should ensure it has the right settings. + # see also notes in wuttapos.terminal.app module, for run_app() + os.environ["WUTTA_CONFIG_FILES"] = os.pathsep.join(config.get_prioritized_files()) + + run_app() diff --git a/src/wuttapos/install.py b/src/wuttapos/install.py index 5821907..e140fd4 100644 --- a/src/wuttapos/install.py +++ b/src/wuttapos/install.py @@ -24,6 +24,7 @@ Install handler for WuttaPOS """ +import os import subprocess import sys @@ -37,14 +38,62 @@ class InstallHandler(base.InstallHandler): template_paths = ["wuttapos:installer-templates"] + store_id = None + terminal_id = None + alongside = False + def do_install_steps(self): # prompt for install type first self.get_install_type() + # install server/terminal dependencies + self.install_app_deps() + # then everything else super().do_install_steps() + def sanity_check(self): + """ + We override this because the normal up-front sanity check + includes a check for the app dir. But we need to delay that + one a bit, so behavior depends on which "install type" we're + doing. + """ + + def check_appdir(self): + """ + We bypass the normal check here, if the current install is for + terminal alongside server. + """ + if self.install_type == "terminal": + + # does appdir exist yet? + appdir = os.path.join(sys.prefix, "app") + if os.path.exists(appdir): + + # install alongside server? + self.alongside = self.prompt_bool( + "install alongside server?", default=False + ) + if self.alongside: + # this mode expects the appdir to exist, so continue + return + + else: + # no appdir - means we should install "everything" for + # the terminal, but we don't support that yet + self.rprint( + f"\n\t[bold red]sorry, full terminal install not yet supported[/bold red]\n" + ) + self.rprint( + f"\n\tPlease install the server, then terminal alongside that.\n" + ) + sys.exit(2) + + # do normal check + super().check_appdir() + def get_install_type(self): # prompt user @@ -55,22 +104,88 @@ class InstallHandler(base.InstallHandler): # remember the answer self.install_type = install_type - if self.install_type != "server": - self.rprint( - "[bold red]sorry, terminal install is not yet implemented[/bold red]\n" - ) - sys.exit(1) + # now we can check the app dir; do this before further questions + self.check_appdir() - # install dependencies + # stop here for server, but we have more questions for terminal if self.install_type == "server": - self.install_server_deps() + return - def install_server_deps(self): - subprocess.check_call( - [sys.executable, "-m", "pip", "install", "WuttaPOS[server]"] + self.rprint( + "\n\n\t[blue]Next you must specify the Store and Terminal IDs.[/blue]" ) + self.rprint( + "\n\tThese should match records in your DB; see the Server app for info.\n" + ) + + # store_id + while not self.store_id: + self.store_id = self.prompt_generic("Store ID") + + # terminal_id + while not self.terminal_id: + self.terminal_id = self.prompt_generic("Terminal ID") + + def install_app_deps(self): + + if self.install_type == "server": + subprocess.check_call( + [sys.executable, "-m", "pip", "install", "WuttaPOS[server]"] + ) + + elif self.install_type == "terminal": + subprocess.check_call( + [sys.executable, "-m", "pip", "install", "WuttaPOS[terminal]"] + ) + + def get_dbinfo(self): + if self.alongside: + return {"dburl": self.config.appdb_engine.url} + + return super().get_dbinfo() def make_template_context(self, dbinfo, **kwargs): context = super().make_template_context(dbinfo, **kwargs) + context["install_type"] = self.install_type + context["store_id"] = self.store_id + context["terminal_id"] = self.terminal_id + return context + + def write_all_config_files(self, appdir, context): + if self.alongside: + # just want to add terminal.conf for this mode + self.write_terminal_conf(appdir, context) + return + + # new app, so write normal files + super().write_all_config_files(appdir, context) + + def write_terminal_conf(self, appdir, context): + term_context = dict(context) + self.make_config_file( + "terminal.conf.mako", os.path.join(appdir, "terminal.conf"), **term_context + ) + + def install_db_schema(self, db_url, appdir=None): + if self.alongside: + # no need to install schema here + return False + + return super().install_db_schema(db_url, appdir=appdir) + + def show_goodbye(self): + """ + Show the final message; this assumes setup completed okay. + + This is normally called by :meth:`run()`. + """ + if self.alongside: + self.rprint("\n\t[bold green]initial setup is complete![/bold green]") + self.rprint("\n\tyou can run the terminal GUI app with:") + self.rprint(f"\n\t[blue]cd {sys.prefix}[/blue]") + self.rprint("\t[blue]bin/wuttapos -c app/terminal.conf run[/blue]\n") + return + + super().show_goodbye() diff --git a/src/wuttapos/installer-templates/terminal.conf.mako b/src/wuttapos/installer-templates/terminal.conf.mako new file mode 100644 index 0000000..51231bf --- /dev/null +++ b/src/wuttapos/installer-templates/terminal.conf.mako @@ -0,0 +1,17 @@ +## -*- mode: conf; -*- + +<%text>############################################################ +# +# ${app_title} - terminal installed alongside the server +# +<%text>############################################################ + +[wutta.config] +require = %(here)s/wutta.conf + +[wuttapos] +store_id = ${store_id} +terminal_id = ${terminal_id} + +[wutta_continuum] +enable_versioning = false diff --git a/src/wuttapos/terminal/app.py b/src/wuttapos/terminal/app.py index a67efca..628cc56 100644 --- a/src/wuttapos/terminal/app.py +++ b/src/wuttapos/terminal/app.py @@ -282,9 +282,11 @@ def main(page: ft.Page): page.go("/login") -# TODO: can we inject config to the main() via ft.app() kwargs somehow? -# pretty sure the `wuttapos open` command is trying to anyway.. -def run_app(config=None): +# TODO: is there any way to inject a config object into the new Flet +# app? if so this would be the place to do it. currently in main() +# it always makes a new config for itself, as workaround. but also +# see notes in the wuttapos.cli.run module +def run_app(): ft.app(target=main, assets_dir=resource_path("wuttapos.terminal:assets")) From 960dbfdee6835d16b67396c13ecaa34ae6ab007a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 3 Jan 2026 20:53:42 -0600 Subject: [PATCH 2/2] =?UTF-8?q?bump:=20version=200.4.0=20=E2=86=92=200.4.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 9e04d82..c2b907f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to WuttaPOS 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.1 (2026-01-03) + +### Fix + +- add basic support for install/setup of terminal + ## v0.4.0 (2026-01-03) First release under the new project. diff --git a/pyproject.toml b/pyproject.toml index 3328535..a6f9069 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "hatchling.build" name = "WuttaPOS" # nb. 0.3.0 was the last release of the previous rattail WuttaPOS app. # pretty sure this will bump to 0.4.0 and then i can remove this note. -version = "0.4.0" +version = "0.4.1" description = "Point of Sale system based on Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}]