Use actual POS batch under the hood
This commit is contained in:
parent
c3c8ae7e94
commit
545f115dc8
|
@ -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():
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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 = ""
|
||||||
|
|
Loading…
Reference in a new issue