Add button for "Adjust Price"
so far no constraints: any user, any item, any price
This commit is contained in:
parent
4ed006a93f
commit
9340e0d1bc
|
@ -42,18 +42,11 @@ class WuttaTxnItem(WuttaControl):
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
|
|
||||||
kw = {}
|
|
||||||
if self.row.void:
|
|
||||||
kw['color'] = 'red'
|
|
||||||
kw['decoration'] = ft.TextDecoration.LINE_THROUGH
|
|
||||||
|
|
||||||
self.major_style = ft.TextStyle(size=self.font_size,
|
self.major_style = ft.TextStyle(size=self.font_size,
|
||||||
weight=ft.FontWeight.BOLD,
|
weight=ft.FontWeight.BOLD)
|
||||||
**kw)
|
|
||||||
|
|
||||||
self.minor_style = ft.TextStyle(size=int(self.font_size * 0.8),
|
self.minor_style = ft.TextStyle(size=int(self.font_size * 0.8),
|
||||||
italic=True,
|
italic=True)
|
||||||
**kw)
|
|
||||||
|
|
||||||
if self.row.row_type == self.enum.POS_ROW_TYPE_SELL:
|
if self.row.row_type == self.enum.POS_ROW_TYPE_SELL:
|
||||||
return self.build_item_sell()
|
return self.build_item_sell()
|
||||||
|
@ -63,22 +56,33 @@ class WuttaTxnItem(WuttaControl):
|
||||||
return self.build_item_tender()
|
return self.build_item_tender()
|
||||||
|
|
||||||
def build_item_sell(self):
|
def build_item_sell(self):
|
||||||
quantity = self.app.render_quantity(self.row.quantity)
|
|
||||||
pretty_price = self.app.render_currency(self.row.txn_price)
|
self.quantity = ft.TextSpan(style=self.minor_style)
|
||||||
|
self.txn_price = ft.TextSpan(style=self.minor_style)
|
||||||
|
|
||||||
|
self.sales_total_style = ft.TextStyle(size=self.font_size,
|
||||||
|
weight=ft.FontWeight.BOLD)
|
||||||
|
|
||||||
|
self.sales_total = ft.TextSpan(style=self.sales_total_style)
|
||||||
|
|
||||||
|
# set initial text display values
|
||||||
|
self.refresh(update=False)
|
||||||
|
|
||||||
return ft.Row(
|
return ft.Row(
|
||||||
[
|
[
|
||||||
ft.Text(
|
ft.Text(
|
||||||
spans=[
|
spans=[
|
||||||
ft.TextSpan(f"{self.row.description}",
|
ft.TextSpan(f"{self.row.description}",
|
||||||
style=self.major_style),
|
style=self.major_style),
|
||||||
ft.TextSpan(f"× {quantity} @ {pretty_price}",
|
ft.TextSpan("× ", style=self.minor_style),
|
||||||
style=self.minor_style),
|
self.quantity,
|
||||||
|
ft.TextSpan(" @ ", style=self.minor_style),
|
||||||
|
self.txn_price,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ft.Text(
|
ft.Text(
|
||||||
spans=[
|
spans=[
|
||||||
ft.TextSpan(self.app.render_currency(self.row.sales_total),
|
self.sales_total,
|
||||||
style=self.major_style),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -106,15 +110,33 @@ class WuttaTxnItem(WuttaControl):
|
||||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
)
|
)
|
||||||
|
|
||||||
def mark_void(self):
|
def refresh(self, update=True):
|
||||||
|
|
||||||
# TODO: how to properly handle this restriction?
|
if self.row.void:
|
||||||
assert self.row.row_type == self.enum.POS_ROW_TYPE_SELL
|
self.major_style.color = 'red'
|
||||||
|
self.major_style.decoration = ft.TextDecoration.LINE_THROUGH
|
||||||
|
self.minor_style.color = 'red'
|
||||||
|
self.minor_style.decoration = ft.TextDecoration.LINE_THROUGH
|
||||||
|
else:
|
||||||
|
self.major_style.color = None
|
||||||
|
self.major_style.decoration = None
|
||||||
|
self.minor_style.color = None
|
||||||
|
self.minor_style.decoration = None
|
||||||
|
|
||||||
self.major_style.color = 'red'
|
if self.row.row_type == self.enum.POS_ROW_TYPE_SELL:
|
||||||
self.major_style.decoration = ft.TextDecoration.LINE_THROUGH
|
self.quantity.text = self.app.render_quantity(self.row.quantity)
|
||||||
|
self.txn_price.text = self.app.render_currency(self.row.txn_price)
|
||||||
|
self.sales_total.text = self.app.render_currency(self.row.sales_total)
|
||||||
|
|
||||||
self.minor_style.color = 'red'
|
if self.row.void:
|
||||||
self.minor_style.decoration = ft.TextDecoration.LINE_THROUGH
|
self.sales_total_style.color = 'red'
|
||||||
|
self.sales_total_style.decoration = ft.TextDecoration.LINE_THROUGH
|
||||||
|
else:
|
||||||
|
if self.row.txn_price < self.row.reg_price:
|
||||||
|
self.sales_total_style.color = 'green'
|
||||||
|
else:
|
||||||
|
self.sales_total_style.color = None
|
||||||
|
self.sales_total_style.decoration = None
|
||||||
|
|
||||||
self.update()
|
if update:
|
||||||
|
self.update()
|
||||||
|
|
|
@ -44,7 +44,8 @@ def make_button(text, font_size=24, font_bold=True, font_weight=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
if not font_weight and font_bold:
|
if not font_weight and font_bold:
|
||||||
font_weight = ft.FontWeight.BOLD
|
font_weight = ft.FontWeight.BOLD
|
||||||
text = ft.Text(text, size=font_size, weight=font_weight)
|
text = ft.Text(text, size=font_size, weight=font_weight,
|
||||||
|
text_align=ft.TextAlign.CENTER)
|
||||||
|
|
||||||
kwargs.setdefault('alignment', ft.alignment.center)
|
kwargs.setdefault('alignment', ft.alignment.center)
|
||||||
kwargs.setdefault('border', ft.border.all(1, 'black'))
|
kwargs.setdefault('border', ft.border.all(1, 'black'))
|
||||||
|
|
|
@ -87,6 +87,16 @@ class WuttaView(ft.View):
|
||||||
kwargs.setdefault('height', 100)
|
kwargs.setdefault('height', 100)
|
||||||
return ft.Image(src=logo, **kwargs)
|
return ft.Image(src=logo, **kwargs)
|
||||||
|
|
||||||
|
def show_snackbar(self, text, bgcolor='yellow', update=True):
|
||||||
|
self.page.snack_bar = ft.SnackBar(ft.Text(text, color='black',
|
||||||
|
size=20,
|
||||||
|
weight=ft.FontWeight.BOLD),
|
||||||
|
bgcolor=bgcolor,
|
||||||
|
duration=1500)
|
||||||
|
self.page.snack_bar.open = True
|
||||||
|
if update:
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
|
||||||
class WuttaViewContainer(ft.Container):
|
class WuttaViewContainer(ft.Container):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -140,10 +140,15 @@ class POSView(WuttaView):
|
||||||
|
|
||||||
if row:
|
if row:
|
||||||
session.commit()
|
session.commit()
|
||||||
self.add_row_item(row)
|
|
||||||
self.items.scroll_to(offset=-1, duration=100)
|
if row.row_type == self.enum.POS_ROW_TYPE_BADPRICE:
|
||||||
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
self.show_snackbar(f"Product has invalid price: {row.item_entry}")
|
||||||
self.reset()
|
|
||||||
|
else:
|
||||||
|
self.add_row_item(row)
|
||||||
|
self.items.scroll_to(offset=-1, duration=100)
|
||||||
|
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
||||||
|
self.reset()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
@ -608,8 +613,7 @@ class POSView(WuttaView):
|
||||||
self.set_quantity.data = None
|
self.set_quantity.data = None
|
||||||
self.set_quantity.value = None
|
self.set_quantity.value = None
|
||||||
elif self.selected_item:
|
elif self.selected_item:
|
||||||
self.selected_item.bgcolor = 'white'
|
self.clear_item_selection()
|
||||||
self.selected_item = None
|
|
||||||
self.main_input.focus()
|
self.main_input.focus()
|
||||||
self.page.update()
|
self.page.update()
|
||||||
|
|
||||||
|
@ -625,39 +629,36 @@ class POSView(WuttaView):
|
||||||
meta_button_width = meta_button_height * 2
|
meta_button_width = meta_button_height * 2
|
||||||
meta_font_size = self.default_font_size
|
meta_font_size = self.default_font_size
|
||||||
|
|
||||||
def meta_button(text, on_click=None, bgcolor='blue', data=None,
|
def meta_button(text, font_size=None, bgcolor='blue', data=None, on_click=None):
|
||||||
font_size=None):
|
return self.make_button(text,
|
||||||
return ft.Container(content=ft.Text(text, size=font_size or meta_font_size,
|
font_size=font_size or meta_font_size,
|
||||||
weight=ft.FontWeight.BOLD),
|
height=meta_button_height,
|
||||||
height=meta_button_height,
|
width=meta_button_width,
|
||||||
width=meta_button_width,
|
bgcolor=bgcolor,
|
||||||
on_click=on_click,
|
data=data,
|
||||||
alignment=ft.alignment.center,
|
on_click=on_click)
|
||||||
border=ft.border.all(1, 'black'),
|
|
||||||
border_radius=ft.border_radius.all(5),
|
|
||||||
bgcolor=bgcolor,
|
|
||||||
data=data)
|
|
||||||
|
|
||||||
self.meta_menu = ft.Container(
|
self.meta_menu = ft.Container(
|
||||||
content=ft.Column(
|
content=ft.Column(
|
||||||
[
|
[
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
meta_button("ITEM", bgcolor='blue', on_click=self.item_click),
|
meta_button("CUST", bgcolor='blue', on_click=self.customer_click),
|
||||||
meta_button("VOID", bgcolor='red', on_click=self.void_click),
|
meta_button("VOID", bgcolor='red', on_click=self.void_click),
|
||||||
],
|
],
|
||||||
spacing=0,
|
spacing=0,
|
||||||
),
|
),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
meta_button("CUST", bgcolor='blue', on_click=self.customer_click),
|
meta_button("ITEM", bgcolor='blue', on_click=self.item_click),
|
||||||
meta_button("MGR", bgcolor='yellow', on_click=self.not_supported),
|
meta_button("MGR", bgcolor='yellow', on_click=self.not_supported),
|
||||||
],
|
],
|
||||||
spacing=0,
|
spacing=0,
|
||||||
),
|
),
|
||||||
ft.Row(
|
ft.Row(
|
||||||
[
|
[
|
||||||
meta_button("TODO", bgcolor='blue', on_click=self.not_supported),
|
meta_button("Adjust\nPrice", font_size=30, bgcolor='yellow',
|
||||||
|
on_click=self.adjust_price_click),
|
||||||
meta_button("TODO", bgcolor='blue', on_click=self.not_supported),
|
meta_button("TODO", bgcolor='blue', on_click=self.not_supported),
|
||||||
],
|
],
|
||||||
spacing=0,
|
spacing=0,
|
||||||
|
@ -849,7 +850,7 @@ class POSView(WuttaView):
|
||||||
|
|
||||||
text = "NOT YET SUPPORTED"
|
text = "NOT YET SUPPORTED"
|
||||||
if not feature and e:
|
if not feature and e:
|
||||||
feature = e.control.content.value
|
feature = e.control.content.value.replace('\n', ' ')
|
||||||
if feature:
|
if feature:
|
||||||
text += f": {feature}"
|
text += f": {feature}"
|
||||||
self.page.snack_bar = ft.SnackBar(ft.Text(text, color='black',
|
self.page.snack_bar = ft.SnackBar(ft.Text(text, color='black',
|
||||||
|
@ -860,6 +861,148 @@ class POSView(WuttaView):
|
||||||
self.page.snack_bar.open = True
|
self.page.snack_bar.open = True
|
||||||
self.page.update()
|
self.page.update()
|
||||||
|
|
||||||
|
def adjust_price_click(self, e):
|
||||||
|
|
||||||
|
if not len(self.items.controls):
|
||||||
|
self.show_snackbar("There are no line items", update=False)
|
||||||
|
self.reset()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.selected_item:
|
||||||
|
self.show_snackbar("Must first select a line item", update=False)
|
||||||
|
self.main_input.focus()
|
||||||
|
self.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
def cancel(e):
|
||||||
|
dlg.open = False
|
||||||
|
self.main_input.focus()
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def clear(e):
|
||||||
|
price_override.value = ''
|
||||||
|
price_override.focus()
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def tenkey_char(key):
|
||||||
|
price_override.value = f"{price_override.value or ''}{key}"
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
|
def confirm(e):
|
||||||
|
dlg.open = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
price = decimal.Decimal(price_override.value)
|
||||||
|
except decimal.InvalidOperation:
|
||||||
|
self.show_snackbar(f"Price is not valid: {price_override.value}", update=False)
|
||||||
|
self.main_input.focus()
|
||||||
|
self.page.update()
|
||||||
|
return
|
||||||
|
|
||||||
|
session = self.app.make_session()
|
||||||
|
user = self.get_current_user(session)
|
||||||
|
handler = self.get_batch_handler()
|
||||||
|
|
||||||
|
row = self.selected_item.data['row']
|
||||||
|
row = session.get(row.__class__, row.uuid)
|
||||||
|
|
||||||
|
new_row = handler.override_price(row, user, price)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
# update screen to reflect new balance
|
||||||
|
batch = row.batch
|
||||||
|
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
||||||
|
|
||||||
|
# update item display
|
||||||
|
self.selected_item.data['row'] = row
|
||||||
|
self.selected_item.content.row = row
|
||||||
|
self.selected_item.content.refresh()
|
||||||
|
self.items.update()
|
||||||
|
|
||||||
|
session.expunge_all()
|
||||||
|
session.close()
|
||||||
|
self.clear_item_selection()
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
price_override = ft.TextField(value=self.main_input.value,
|
||||||
|
text_size=32,
|
||||||
|
text_style=ft.TextStyle(weight=ft.FontWeight.BOLD),
|
||||||
|
autofocus=True,
|
||||||
|
on_submit=confirm)
|
||||||
|
|
||||||
|
dlg = ft.AlertDialog(
|
||||||
|
modal=True,
|
||||||
|
title=ft.Text("Adjust Price"),
|
||||||
|
content=ft.Container(
|
||||||
|
ft.Column(
|
||||||
|
[
|
||||||
|
ft.Divider(),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Reg Price:",
|
||||||
|
size=32, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(self.app.render_currency(self.selected_item.data['row'].reg_price),
|
||||||
|
size=32, weight=ft.FontWeight.BOLD),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ft.Row(),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("Txn Price:",
|
||||||
|
size=32, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(self.app.render_currency(self.selected_item.data['row'].txn_price),
|
||||||
|
size=32, weight=ft.FontWeight.BOLD),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ft.Row(),
|
||||||
|
ft.Row(),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Text("New Price:",
|
||||||
|
size=32, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.VerticalDivider(),
|
||||||
|
ft.Text("$", size=32, weight=ft.FontWeight.BOLD),
|
||||||
|
price_override,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ft.Row(),
|
||||||
|
ft.Row(),
|
||||||
|
ft.Row(
|
||||||
|
[
|
||||||
|
WuttaTenkeyMenu(self.config, simple=True,
|
||||||
|
on_char=tenkey_char,
|
||||||
|
on_enter=confirm),
|
||||||
|
self.make_button("Clear",
|
||||||
|
height=self.default_button_size * 0.8,
|
||||||
|
width=self.default_button_size * 1.2,
|
||||||
|
on_click=clear),
|
||||||
|
],
|
||||||
|
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.START,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
height=700,
|
||||||
|
width=550,
|
||||||
|
),
|
||||||
|
actions=[
|
||||||
|
self.make_button("Cancel",
|
||||||
|
height=self.default_button_size * 0.8,
|
||||||
|
width=self.default_button_size * 1.2,
|
||||||
|
on_click=cancel),
|
||||||
|
self.make_button("Confirm",
|
||||||
|
bgcolor='blue',
|
||||||
|
height=self.default_button_size * 0.8,
|
||||||
|
width=self.default_button_size * 1.2,
|
||||||
|
on_click=confirm),
|
||||||
|
],
|
||||||
|
actions_alignment=ft.MainAxisAlignment.END,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.page.dialog = dlg
|
||||||
|
dlg.open = True
|
||||||
|
self.page.update()
|
||||||
|
|
||||||
def add_row_item(self, row):
|
def add_row_item(self, row):
|
||||||
|
|
||||||
# TODO: row types ugh
|
# TODO: row types ugh
|
||||||
|
@ -872,7 +1015,7 @@ class POSView(WuttaView):
|
||||||
ft.Container(
|
ft.Container(
|
||||||
content=WuttaTxnItem(self.config, row),
|
content=WuttaTxnItem(self.config, row),
|
||||||
border=ft.border.only(bottom=ft.border.BorderSide(1, 'gray')),
|
border=ft.border.only(bottom=ft.border.BorderSide(1, 'gray')),
|
||||||
padding=ft.padding.only(0, 5, 0, 5),
|
padding=ft.padding.only(5, 5, 5, 5),
|
||||||
on_click=self.list_item_click,
|
on_click=self.list_item_click,
|
||||||
data={'row': row},
|
data={'row': row},
|
||||||
key=row.uuid,
|
key=row.uuid,
|
||||||
|
@ -883,10 +1026,10 @@ class POSView(WuttaView):
|
||||||
|
|
||||||
def select_txn_item(self, item):
|
def select_txn_item(self, item):
|
||||||
if self.selected_item:
|
if self.selected_item:
|
||||||
self.selected_item.bgcolor = 'white'
|
self.clear_item_selection()
|
||||||
|
|
||||||
item.bgcolor = 'blue'
|
|
||||||
self.selected_item = item
|
self.selected_item = item
|
||||||
|
self.selected_item.bgcolor = 'blue'
|
||||||
self.page.update()
|
self.page.update()
|
||||||
|
|
||||||
def void_click(self, e):
|
def void_click(self, e):
|
||||||
|
@ -928,9 +1071,9 @@ class POSView(WuttaView):
|
||||||
row = session.get(row.__class__, row.uuid)
|
row = session.get(row.__class__, row.uuid)
|
||||||
handler.void_row(row, user)
|
handler.void_row(row, user)
|
||||||
self.selected_item.data['row'] = row
|
self.selected_item.data['row'] = row
|
||||||
self.selected_item.content.mark_void()
|
self.selected_item.content.row = row
|
||||||
self.selected_item.bgcolor = 'white'
|
self.selected_item.content.refresh()
|
||||||
self.selected_item = None
|
self.clear_item_selection()
|
||||||
|
|
||||||
# update screen to reflect new balance
|
# update screen to reflect new balance
|
||||||
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
||||||
|
@ -1163,6 +1306,12 @@ class POSView(WuttaView):
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
def clear_item_selection(self):
|
||||||
|
if self.selected_item:
|
||||||
|
self.selected_item.bgcolor = 'white'
|
||||||
|
self.selected_item.content.refresh()
|
||||||
|
self.selected_item = None
|
||||||
|
|
||||||
def clear_all(self):
|
def clear_all(self):
|
||||||
self.items.controls.clear()
|
self.items.controls.clear()
|
||||||
self.txn_total.value = None
|
self.txn_total.value = None
|
||||||
|
|
Loading…
Reference in a new issue