From 763141fccc6e82431b842a6c16079787965a0fc5 Mon Sep 17 00:00:00 2001 From: "Borrell.Guillem@bcg.com" Date: Mon, 6 Nov 2023 22:05:11 +0100 Subject: [PATCH] changes that require resetting the db. Staging --- .gitignore | 3 ++ docs/automation.md | 7 +++- docs/terminals.md | 2 +- src/retailtwin/cli/pos/main.py | 51 ++++++++++++++++++++------ src/retailtwin/cli/pos/models.py | 5 ++- src/retailtwin/sql/views/inventory.sql | 1 + 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 68bc17f..e06e50d 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,6 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + + +*.db \ No newline at end of file diff --git a/docs/automation.md b/docs/automation.md index 677a817..078d4a3 100644 --- a/docs/automation.md +++ b/docs/automation.md @@ -16,4 +16,9 @@ For each automation you should decide the execution time, with many possible cho # Triggers to act on a given condition -Let's open the `stock` terminal run a little experiment. \ No newline at end of file +Let's open the `stock` terminal run a little experiment. + +# If it works, don't touch it + +Creating robust automations is key for the operations of any corporation. Automations tend to become systemic, and if somethigs works as expected, there's a strong motivation to touch it as little as possible. Even nowadays it's common to find out that the automations dealing with payments are still implemented with COBOL, and in that case, the process to migrate from COBOL to other "enterprise" language like Java, takes half a decade to complete. + diff --git a/docs/terminals.md b/docs/terminals.md index e31e261..169e495 100644 --- a/docs/terminals.md +++ b/docs/terminals.md @@ -105,7 +105,7 @@ There's a 99% chance that the future Windows version released in 2033 is still a !!! example - The Airbus A320 civil aircraft was developed in the eighties. The Multipurpose Control and Display Unit (MCDU) is a panel that the flight crew use to interact with the onboard computer. + The Airbus A320 civil aircraft was developed in the eighties. The Multipurpose Control and Display Unit (MCDU) is a panel that the flight crew use to interact with the onboard computer. Together with the autopilot, it's one of the components of the aircrafts that the crew spends most time interacting with. ![mcdu.png](img/A320cockpit.png) diff --git a/src/retailtwin/cli/pos/main.py b/src/retailtwin/cli/pos/main.py index 5b72880..fccc4fa 100644 --- a/src/retailtwin/cli/pos/main.py +++ b/src/retailtwin/cli/pos/main.py @@ -4,7 +4,7 @@ Terminal that mimics what a Point of Sale may operate like. import typer import polars as pl from retailtwin.cli.db import query_local_batches -from retailtwin.cli.pos.models import Base, Sync, Direction, Cart, Item +from retailtwin.cli.pos.models import Base, Sync, Direction, Cart, Item, ItemOnCart from retailtwin.utils import db_uri_from_session from datetime import datetime @@ -23,6 +23,11 @@ HELP = """ # Retail twin POS This is a simple terminal that simulates a dumb PoS with a single-line screen. + +* **n**: Opens a new cart +* **l**: Lists carts +* **r**: Refreshes with the central database +* **q**: Quits the terminal """ CACHE_FILE_URL = "sqlite:///pos.db" @@ -50,16 +55,17 @@ def sync(session: Session, db_remote_uri: str, location: int): inventory.select( [ pl.col("upc"), - pl.col("price"), - pl.col("discount_definition").alias("discount"), + pl.col("unitprice"), + pl.col("discount_definition"), + pl.col("discount_name"), pl.col("received"), pl.col("received").max().over("upc").alias("max_received"), ] ) .filter(pl.col("received") == pl.col("max_received")) - .select([pl.col("upc"), pl.col("price"), pl.col("discount")]) - .with_columns(pl.col("price").str.strip().cast(pl.Float32) * 100) - .with_columns(pl.col("price").cast(pl.Int32)) + .select([pl.col("upc"), pl.col("unitprice"), pl.col("discount")]) + .with_columns(pl.col("unitprice").str.strip().cast(pl.Float32) * 100) + .with_columns(pl.col("unitprice").cast(pl.Int32)) ) items.write_database( "items", db_local_uri, if_exists="replace", engine="sqlalchemy" @@ -125,12 +131,15 @@ def sync(session: Session, db_remote_uri: str, location: int): session.commit() +def checkout(session: Session, cart_id: int): + return None + + def load_items(session: Session): return [str(it) for it in session.scalars(select(Item.upc)).all()] def handle_command(command: str, cart_id: int, item_id: int, **kwargs): - print(command, cart_id, item_id) session = kwargs["session"] if command == "n": # Start a new cart @@ -140,13 +149,32 @@ def handle_command(command: str, cart_id: int, item_id: int, **kwargs): return cart.id, "", "" + elif command == "l": # List carts + carts = pl.read_database( + "select * from carts", db_uri_from_session(session), engine="adbc" + ) + print(carts) + return "", "", "" + + # Handle pushing items to the cart + elif cart_id is not None and item_id: + session.add(ItemOnCart(cart=cart_id, upc=item_id, quantity=command)) + print(f"{item_id} -- {command}") + return cart_id, None, "" + # Handle adding a new item elif cart_id is not None: # Check if the upc is in the database - if session.scalar(select(Item).filter(Item.upc == int(command))).one_or_none(): - return cart_id, item_id, "" + if command == "e": + error = checkout(session=session, cart_id=cart_id) + return None, None, error else: - return cart_id, "", "ERROR: Code not found" + if session.scalar(select(Item).filter(Item.upc == int(command))): + item_id = command + print(f"selecting {item_id}") + return cart_id, item_id, "" + else: + return cart_id, "", "ERROR: Code not found" else: cart_id, item_id if item_id else "", "" @@ -181,7 +209,7 @@ def main(db_uri: str, location: int): else: prompt = f"[{item_id}# quantity]>" elif cart_id and not item_id: - prompt = f"[{cart_id}# code]> " + prompt = f"[{cart_id}# code, e for chekout]> " else: prompt = "#> " @@ -210,6 +238,7 @@ def main(db_uri: str, location: int): db_remote_uri=db_uri, session=session, ) + print(cart_id, item_id, error) print("GoodBye!") diff --git a/src/retailtwin/cli/pos/models.py b/src/retailtwin/cli/pos/models.py index d35fc1f..4ca0726 100644 --- a/src/retailtwin/cli/pos/models.py +++ b/src/retailtwin/cli/pos/models.py @@ -33,7 +33,10 @@ class Item(Base): __tablename__ = "items" upc: Mapped[int] = mapped_column(primary_key=True) unitprice: Mapped[Decimal] = mapped_column(Numeric(9, 2), nullable=False) - discount: Mapped[Dict[str, Dict[str, int]]] = mapped_column(JSON, nullable=True) + discount_name: Mapped[str] + discount_definition: Mapped[Dict[str, Dict[str, int]]] = mapped_column( + JSON, nullable=True + ) class Customer(Base): diff --git a/src/retailtwin/sql/views/inventory.sql b/src/retailtwin/sql/views/inventory.sql index 41a5d4c..405c701 100644 --- a/src/retailtwin/sql/views/inventory.sql +++ b/src/retailtwin/sql/views/inventory.sql @@ -9,6 +9,7 @@ create or replace view inventory as ( date(b.best_until at time zone 'UTC') as best_until, i.quantity as quantity, i."location" as "location", + d.name as discount_name, d.definition::text as discount_definition from itemsonshelf i join batches b on i.batch = b.id