changes that require resetting the db. Staging

This commit is contained in:
Borrell.Guillem@bcg.com 2023-11-06 22:05:11 +01:00
parent 53788b4180
commit 763141fccc
6 changed files with 55 additions and 14 deletions

3
.gitignore vendored
View file

@ -158,3 +158,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear # 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. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
*.db

View file

@ -17,3 +17,8 @@ For each automation you should decide the execution time, with many possible cho
# Triggers to act on a given condition # Triggers to act on a given condition
Let's open the `stock` terminal run a little experiment. 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.

View file

@ -105,7 +105,7 @@ There's a 99% chance that the future Windows version released in 2033 is still a
!!! example !!! 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) ![mcdu.png](img/A320cockpit.png)

View file

@ -4,7 +4,7 @@ Terminal that mimics what a Point of Sale may operate like.
import typer import typer
import polars as pl import polars as pl
from retailtwin.cli.db import query_local_batches 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 retailtwin.utils import db_uri_from_session
from datetime import datetime from datetime import datetime
@ -23,6 +23,11 @@ HELP = """
# Retail twin POS # Retail twin POS
This is a simple terminal that simulates a dumb PoS with a single-line screen. 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" CACHE_FILE_URL = "sqlite:///pos.db"
@ -50,16 +55,17 @@ def sync(session: Session, db_remote_uri: str, location: int):
inventory.select( inventory.select(
[ [
pl.col("upc"), pl.col("upc"),
pl.col("price"), pl.col("unitprice"),
pl.col("discount_definition").alias("discount"), pl.col("discount_definition"),
pl.col("discount_name"),
pl.col("received"), pl.col("received"),
pl.col("received").max().over("upc").alias("max_received"), pl.col("received").max().over("upc").alias("max_received"),
] ]
) )
.filter(pl.col("received") == pl.col("max_received")) .filter(pl.col("received") == pl.col("max_received"))
.select([pl.col("upc"), pl.col("price"), pl.col("discount")]) .select([pl.col("upc"), pl.col("unitprice"), pl.col("discount")])
.with_columns(pl.col("price").str.strip().cast(pl.Float32) * 100) .with_columns(pl.col("unitprice").str.strip().cast(pl.Float32) * 100)
.with_columns(pl.col("price").cast(pl.Int32)) .with_columns(pl.col("unitprice").cast(pl.Int32))
) )
items.write_database( items.write_database(
"items", db_local_uri, if_exists="replace", engine="sqlalchemy" "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() session.commit()
def checkout(session: Session, cart_id: int):
return None
def load_items(session: Session): def load_items(session: Session):
return [str(it) for it in session.scalars(select(Item.upc)).all()] return [str(it) for it in session.scalars(select(Item.upc)).all()]
def handle_command(command: str, cart_id: int, item_id: int, **kwargs): def handle_command(command: str, cart_id: int, item_id: int, **kwargs):
print(command, cart_id, item_id)
session = kwargs["session"] session = kwargs["session"]
if command == "n": # Start a new cart if command == "n": # Start a new cart
@ -140,10 +149,29 @@ def handle_command(command: str, cart_id: int, item_id: int, **kwargs):
return cart.id, "", "" 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 # Handle adding a new item
elif cart_id is not None: elif cart_id is not None:
# Check if the upc is in the database # Check if the upc is in the database
if session.scalar(select(Item).filter(Item.upc == int(command))).one_or_none(): if command == "e":
error = checkout(session=session, cart_id=cart_id)
return None, None, error
else:
if session.scalar(select(Item).filter(Item.upc == int(command))):
item_id = command
print(f"selecting {item_id}")
return cart_id, item_id, "" return cart_id, item_id, ""
else: else:
return cart_id, "", "ERROR: Code not found" return cart_id, "", "ERROR: Code not found"
@ -181,7 +209,7 @@ def main(db_uri: str, location: int):
else: else:
prompt = f"[{item_id}# quantity]>" prompt = f"[{item_id}# quantity]>"
elif cart_id and not item_id: elif cart_id and not item_id:
prompt = f"[{cart_id}# code]> " prompt = f"[{cart_id}# code, e for chekout]> "
else: else:
prompt = "#> " prompt = "#> "
@ -210,6 +238,7 @@ def main(db_uri: str, location: int):
db_remote_uri=db_uri, db_remote_uri=db_uri,
session=session, session=session,
) )
print(cart_id, item_id, error)
print("GoodBye!") print("GoodBye!")

View file

@ -33,7 +33,10 @@ class Item(Base):
__tablename__ = "items" __tablename__ = "items"
upc: Mapped[int] = mapped_column(primary_key=True) upc: Mapped[int] = mapped_column(primary_key=True)
unitprice: Mapped[Decimal] = mapped_column(Numeric(9, 2), nullable=False) 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): class Customer(Base):

View file

@ -9,6 +9,7 @@ create or replace view inventory as (
date(b.best_until at time zone 'UTC') as best_until, date(b.best_until at time zone 'UTC') as best_until,
i.quantity as quantity, i.quantity as quantity,
i."location" as "location", i."location" as "location",
d.name as discount_name,
d.definition::text as discount_definition d.definition::text as discount_definition
from itemsonshelf i from itemsonshelf i
join batches b on i.batch = b.id join batches b on i.batch = b.id