Use actual POS batch under the hood

This commit is contained in:
Lance Edgar 2023-09-23 11:19:42 -05:00
parent c3c8ae7e94
commit 545f115dc8
3 changed files with 195 additions and 31 deletions

View file

@ -36,6 +36,8 @@ import wuttapos
def main(page: ft.Page): def main(page: ft.Page):
config = make_config() config = make_config()
app = config.get_app()
model = app.model
page.title = f"WuttaPOS v{wuttapos.__version__}" page.title = f"WuttaPOS v{wuttapos.__version__}"
page.window_full_screen = True page.window_full_screen = True
@ -50,6 +52,16 @@ def main(page: ft.Page):
if os.path.exists(path): if os.path.exists(path):
with open(path, 'rt') as f: with open(path, 'rt') as f:
page.shared = json.loads(f.read()) page.shared = json.loads(f.read())
page.shared.pop('txn_display', None) # TODO
if page.shared and page.shared.get('user_uuid'):
handler = app.get_batch_handler('pos')
session = app.make_session()
user = session.get(model.User, page.shared['user_uuid'])
# TODO: should also filter this by terminal
batch = handler.get_current_batch(user, create=False)
if batch:
page.shared['txn_display'] = batch.id_str
session.close()
def clean_exit(): def clean_exit():
if not config.production(): if not config.production():

View file

@ -40,6 +40,7 @@ class WuttaView(ft.View):
self.config = config self.config = config
self.app = self.config.get_app() self.app = self.config.get_app()
self.model = self.app.model self.model = self.app.model
self.enum = self.app.enum
controls = self.build_controls() controls = self.build_controls()
self.controls = [ self.controls = [
@ -52,7 +53,8 @@ class WuttaView(ft.View):
return [self.build_header()] return [self.build_header()]
def build_header(self): def build_header(self):
return WuttaHeader(self.config) self.header = WuttaHeader(self.config)
return self.header
class WuttaViewContainer(ft.Container): class WuttaViewContainer(ft.Container):

View file

@ -43,9 +43,17 @@ class POSView(WuttaView):
self.items = ft.ListView() self.items = ft.ListView()
self.items_container = ft.Container(content=self.items, self.txn_total = ft.Text("", size=40)
padding=ft.padding.only(10, 0, 10, 0),
expand=1) self.items_column = ft.Column(
controls=[
ft.Container(content=self.items,
padding=ft.padding.only(10, 0, 10, 0)),
ft.Row([self.txn_total],
alignment=ft.MainAxisAlignment.END),
],
expand=1,
)
def tenkey_click(e): def tenkey_click(e):
value = e.control.content.value value = e.control.content.value
@ -131,16 +139,17 @@ class POSView(WuttaView):
meta_button_width = meta_button_height * 2 meta_button_width = meta_button_height * 2
meta_font_size = tenkey_font_size meta_font_size = tenkey_font_size
def meta_button(text): def meta_button(text, on_click=None, bgcolor='yellow'):
return ft.Container(content=ft.Text(text, size=meta_font_size, return ft.Container(content=ft.Text(text, size=meta_font_size,
weight=ft.FontWeight.BOLD), weight=ft.FontWeight.BOLD),
height=meta_button_height, height=meta_button_height,
width=meta_button_width, width=meta_button_width,
# on_click=meta_click, # on_click=meta_click,
on_click=on_click,
alignment=ft.alignment.center, alignment=ft.alignment.center,
border=ft.border.all(1, 'black'), border=ft.border.all(1, 'black'),
border_radius=ft.border_radius.all(5), border_radius=ft.border_radius.all(5),
bgcolor='orange') bgcolor=bgcolor)
self.meta_menu = ft.Container( self.meta_menu = ft.Container(
content=ft.Column( content=ft.Column(
@ -148,7 +157,7 @@ class POSView(WuttaView):
ft.Row( ft.Row(
[ [
meta_button("MGR"), meta_button("MGR"),
meta_button("VOID"), meta_button("VOID", bgcolor='red', on_click=self.void_click),
], ],
spacing=0, spacing=0,
), ),
@ -178,6 +187,36 @@ class POSView(WuttaView):
), ),
expand=0) expand=0)
context_button_height = tenkey_button_size
context_button_width = context_button_height * 2
context_font_size = tenkey_font_size
def context_button(text, on_click=None):
return ft.Container(content=ft.Text(text, size=context_font_size,
weight=ft.FontWeight.BOLD),
height=context_button_height,
width=context_button_width,
on_click=on_click,
alignment=ft.alignment.center,
border=ft.border.all(1, 'black'),
border_radius=ft.border_radius.all(5),
bgcolor='orange')
self.context_menu = ft.Container(
content=ft.Column(
[
ft.Row(
[
context_button("CASH", on_click=self.tender_click),
context_button("CHECK", on_click=self.tender_click),
],
spacing=0,
),
],
spacing=0,
),
expand=0)
return [ return [
self.build_header(), self.build_header(),
@ -192,9 +231,18 @@ class POSView(WuttaView):
ft.Row( ft.Row(
[ [
self.items_container, self.items_column,
self.tenkey_menu, ft.Column(
self.meta_menu, [
ft.Row(
[
self.tenkey_menu,
self.meta_menu,
],
),
self.context_menu,
],
),
], ],
vertical_alignment=ft.CrossAxisAlignment.START, vertical_alignment=ft.CrossAxisAlignment.START,
), ),
@ -205,29 +253,130 @@ class POSView(WuttaView):
kwargs.setdefault('size', 24) kwargs.setdefault('size', 24)
return ft.Text(*args, **kwargs) return ft.Text(*args, **kwargs)
def main_submit(self, e): def did_mount(self):
value = self.main_input.value model = self.model
session = self.app.make_session() session = self.app.make_session()
product = self.app.get_products_handler().locate_product_for_entry(session, value) handler = self.app.get_batch_handler('pos')
if product: user = session.get(model.User, self.page.shared['user_uuid'])
price = product.current_price or product.regular_price batch = handler.get_current_batch(user, create=True)
pretty_price = self.app.render_currency(price.price) self.page.shared['txn_display'] = batch.id_str
self.items.controls.append( self.items.controls.clear()
ft.Container( for row in batch.active_rows():
content=ft.Row( self.add_row_item(row)
[
ft.Row([ self.txn_total.value = self.app.render_currency(batch.sales_total)
self.make_text(f"{product}"),
self.make_text(f"× 1 @ {pretty_price}", weight=None, italic=True), session.commit()
]), session.close()
self.make_text(pretty_price),
], def add_row_item(self, row):
alignment=ft.MainAxisAlignment.SPACE_BETWEEN, quantity = self.app.render_quantity(row.quantity)
), pretty_price = self.app.render_currency(row.txn_price)
border=ft.border.only(bottom=ft.border.BorderSide(1, 'gray')), self.items.controls.append(
padding=ft.padding.only(0, 5, 0, 5))) ft.Container(
content=ft.Row(
[
ft.Row([
self.make_text(f"{row.description}"),
self.make_text(f"× {quantity} @ {pretty_price}",
weight=None, italic=True, size=20),
]),
self.make_text(pretty_price),
],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
),
border=ft.border.only(bottom=ft.border.BorderSide(1, 'gray')),
padding=ft.padding.only(0, 5, 0, 5)))
def void_click(self, e):
def confirm(e):
dlg.open = False
model = self.model
session = self.app.make_session()
handler = self.app.get_batch_handler('pos')
user = session.get(model.User, self.page.shared['user_uuid'])
batch = handler.get_current_batch(user, create=True)
# void current batch
handler.void_batch(batch, user)
session.flush()
# make new batch
batch = handler.get_current_batch(user, create=True)
# commit changes
session.commit()
session.close()
# reset txn display
self.items.controls.clear()
self.txn_total.value = None
self.page.shared['txn_display'] = batch.id_str
self.header.update_txn_display()
# TODO: not clear why must call update() for header too?
self.header.update()
self.page.update()
def cancel(e):
dlg.open = False
self.page.update()
dlg = ft.AlertDialog(
modal=True,
title=ft.Text("Confirm VOID"),
content=ft.Text("Really VOID this transaction?"),
actions=[
ft.TextButton("Yes", on_click=confirm),
ft.TextButton("No", on_click=cancel),
])
self.page.dialog = dlg
dlg.open = True
self.page.update()
def tender_click(self, e):
model = self.model
session = self.app.make_session()
handler = self.app.get_batch_handler('pos')
user = session.get(model.User, self.page.shared['user_uuid'])
batch = handler.get_current_batch(user)
# tender / execute batch
tender = e.control.content.value
handler.tender_and_execute(batch, user, tender)
# make new batch
batch = handler.get_current_batch(user, create=True)
session.commit()
session.close()
# reset txn display
self.items.controls.clear()
self.txn_total.value = None
self.page.shared['txn_display'] = batch.id_str
self.header.update_txn_display()
# TODO: not clear why must call update() for header too?
self.header.update()
self.main_input.focus()
self.page.update()
def main_submit(self, e):
handler = self.app.get_batch_handler('pos')
session = self.app.make_session()
model = self.model
user = session.get(model.User, self.page.shared['user_uuid'])
batch = handler.get_current_batch(user)
value = self.main_input.value
row = handler.process_entry(batch, value)
if row:
self.add_row_item(row)
self.txn_total.value = self.app.render_currency(batch.sales_total)
else: else:
self.page.snack_bar = ft.SnackBar(ft.Text(f"UNRECOGNIZED: {value}", self.page.snack_bar = ft.SnackBar(ft.Text(f"UNRECOGNIZED: {value}",
@ -237,6 +386,7 @@ class POSView(WuttaView):
duration=1500) duration=1500)
self.page.snack_bar.open = True self.page.snack_bar.open = True
session.commit()
session.close() session.close()
self.main_input.value = "" self.main_input.value = ""