changes that require resetting the db. Staging
This commit is contained in:
parent
53788b4180
commit
763141fccc
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -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
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,13 +149,32 @@ 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":
|
||||||
return cart_id, item_id, ""
|
error = checkout(session=session, cart_id=cart_id)
|
||||||
|
return None, None, error
|
||||||
else:
|
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:
|
else:
|
||||||
cart_id, item_id if item_id else "", ""
|
cart_id, item_id if item_id else "", ""
|
||||||
|
@ -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!")
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue