Add support for "void line"
also abstract the rendering of a line item to separate class, so can override later etc.
This commit is contained in:
parent
d969d5c994
commit
7a8dce69b3
93
wuttapos/controls/txnitem.py
Normal file
93
wuttapos/controls/txnitem.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# WuttaPOS -- Pythonic Point of Sale System
|
||||
# Copyright © 2023 Lance Edgar
|
||||
#
|
||||
# This file is part of WuttaPOS.
|
||||
#
|
||||
# WuttaPOS is free software: you can redistribute it and/or modify it under the
|
||||
# terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# WuttaPOS is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# WuttaPOS. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
WuttaPOS - txn item control
|
||||
"""
|
||||
|
||||
import flet as ft
|
||||
|
||||
from .base import WuttaControl
|
||||
|
||||
|
||||
class WuttaTxnItem(WuttaControl):
|
||||
"""
|
||||
Control for displaying a transaction line item within main POS
|
||||
items list.
|
||||
"""
|
||||
font_size = 24
|
||||
|
||||
def __init__(self, config, row, *args, **kwargs):
|
||||
super().__init__(config, *args, **kwargs)
|
||||
self.row = row
|
||||
|
||||
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,
|
||||
weight=ft.FontWeight.BOLD,
|
||||
**kw)
|
||||
|
||||
self.minor_style = ft.TextStyle(size=int(self.font_size * 0.8),
|
||||
italic=True,
|
||||
**kw)
|
||||
|
||||
quantity = self.app.render_quantity(self.row.quantity)
|
||||
pretty_price = self.app.render_currency(self.row.txn_price)
|
||||
|
||||
return ft.Row(
|
||||
[
|
||||
ft.Row([
|
||||
|
||||
ft.Text(
|
||||
spans=[
|
||||
ft.TextSpan(f"{self.row.description}",
|
||||
style=self.major_style),
|
||||
ft.TextSpan(f"× {quantity} @ {pretty_price}",
|
||||
style=self.minor_style),
|
||||
],
|
||||
),
|
||||
]),
|
||||
ft.Text(
|
||||
spans=[
|
||||
ft.TextSpan(self.app.render_currency(self.row.sales_total),
|
||||
style=self.major_style),
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||
)
|
||||
|
||||
def mark_void(self):
|
||||
|
||||
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
|
||||
|
||||
self.update()
|
|
@ -33,6 +33,7 @@ import flet as ft
|
|||
from .base import WuttaView
|
||||
from wuttapos.controls.custlookup import WuttaCustomerLookup
|
||||
from wuttapos.controls.itemlookup import WuttaProductLookup
|
||||
from wuttapos.controls.txnitem import WuttaTxnItem
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -63,22 +64,24 @@ class POSView(WuttaView):
|
|||
for control in self.informed_controls:
|
||||
control.informed_refresh()
|
||||
|
||||
def reset(self, e=None, clear_quantity=True, update=True):
|
||||
def reset(self, e=None, clear_quantity=True):
|
||||
"""
|
||||
This is a convenience method, meant only to clear the main
|
||||
input and set focus to it. Will also update() the page
|
||||
by default.
|
||||
input and set focus to it. Will also update() the page.
|
||||
|
||||
The ``e`` arg is ignored and accepted only so this method may
|
||||
be registered as an event handler, e.g. ``on_cancel``.
|
||||
"""
|
||||
# clear set (@) quantity
|
||||
if clear_quantity:
|
||||
self.set_quantity.data = None
|
||||
self.set_quantity.value = None
|
||||
|
||||
# clear/focus main input
|
||||
self.main_input.value = ''
|
||||
self.main_input.focus()
|
||||
if update:
|
||||
self.page.update()
|
||||
|
||||
self.page.update()
|
||||
|
||||
def set_customer(self, customer, batch=None):
|
||||
session = self.app.get_session(customer)
|
||||
|
@ -137,7 +140,7 @@ class POSView(WuttaView):
|
|||
if row:
|
||||
session.commit()
|
||||
self.add_row_item(row)
|
||||
self.items.scroll_to(offset=-1, duration=250)
|
||||
self.items.scroll_to(offset=-1, duration=100)
|
||||
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
||||
self.reset()
|
||||
|
||||
|
@ -490,7 +493,9 @@ class POSView(WuttaView):
|
|||
text_style=ft.TextStyle(weight=ft.FontWeight.BOLD),
|
||||
autofocus=True)
|
||||
|
||||
self.selected_item = None
|
||||
self.items = ft.ListView(
|
||||
item_extent=50,
|
||||
height=800,
|
||||
)
|
||||
|
||||
|
@ -525,6 +530,9 @@ class POSView(WuttaView):
|
|||
elif self.set_quantity.data is not None:
|
||||
self.set_quantity.data = None
|
||||
self.set_quantity.value = None
|
||||
elif self.selected_item:
|
||||
self.selected_item.bgcolor = 'white'
|
||||
self.selected_item = None
|
||||
self.main_input.focus()
|
||||
self.page.update()
|
||||
|
||||
|
@ -566,12 +574,30 @@ class POSView(WuttaView):
|
|||
|
||||
self.page.update()
|
||||
|
||||
elif value == '↑':
|
||||
self.items.scroll_to(delta=-50, duration=250)
|
||||
elif value == '↑': # UP
|
||||
|
||||
# select previous item, if selection in progress
|
||||
if self.selected_item:
|
||||
i = self.items.controls.index(self.selected_item)
|
||||
if i > 0:
|
||||
self.items.scroll_to(delta=-50, duration=100)
|
||||
self.select_txn_item(self.items.controls[i - 1])
|
||||
return
|
||||
|
||||
self.items.scroll_to(delta=-50, duration=100)
|
||||
self.page.update()
|
||||
|
||||
elif value == '↓':
|
||||
self.items.scroll_to(delta=50, duration=250)
|
||||
|
||||
# select next item, if selection in progress
|
||||
if self.selected_item:
|
||||
i = self.items.controls.index(self.selected_item)
|
||||
if (i + 1) < len(self.items.controls):
|
||||
self.items.scroll_to(delta=50, duration=100)
|
||||
self.select_txn_item(self.items.controls[i + 1])
|
||||
return
|
||||
|
||||
self.items.scroll_to(delta=50, duration=100)
|
||||
self.page.update()
|
||||
|
||||
else:
|
||||
|
@ -580,11 +606,11 @@ class POSView(WuttaView):
|
|||
self.page.update()
|
||||
|
||||
def up_long_press(e):
|
||||
self.items.scroll_to(delta=-500, duration=250)
|
||||
self.items.scroll_to(delta=-500, duration=100)
|
||||
self.page.update()
|
||||
|
||||
def down_long_press(e):
|
||||
self.items.scroll_to(delta=500, duration=250)
|
||||
self.items.scroll_to(delta=500, duration=100)
|
||||
self.page.update()
|
||||
|
||||
tenkey_button_size = self.default_button_size
|
||||
|
@ -894,25 +920,26 @@ class POSView(WuttaView):
|
|||
if row.row_type not in ('sell',):
|
||||
return
|
||||
|
||||
quantity = self.app.render_quantity(row.quantity)
|
||||
pretty_price = self.app.render_currency(row.txn_price)
|
||||
self.items.controls.append(
|
||||
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(self.app.render_currency(row.sales_total)),
|
||||
],
|
||||
alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
|
||||
),
|
||||
content=WuttaTxnItem(self.config, row),
|
||||
border=ft.border.only(bottom=ft.border.BorderSide(1, 'gray')),
|
||||
padding=ft.padding.only(0, 5, 0, 5),
|
||||
on_click=self.list_item_click,
|
||||
data={'row': row},
|
||||
key=row.uuid,
|
||||
))
|
||||
|
||||
def list_item_click(self, e):
|
||||
self.select_txn_item(e.control)
|
||||
|
||||
def select_txn_item(self, item):
|
||||
if self.selected_item:
|
||||
self.selected_item.bgcolor = 'white'
|
||||
|
||||
item.bgcolor = 'blue'
|
||||
self.selected_item = item
|
||||
self.page.update()
|
||||
|
||||
def void_click(self, e):
|
||||
|
||||
|
@ -924,12 +951,39 @@ class POSView(WuttaView):
|
|||
user = self.get_current_user(session)
|
||||
batch = handler.get_current_batch(user, create=True)
|
||||
|
||||
# void current batch
|
||||
handler.void_batch(batch, user)
|
||||
if self.selected_item:
|
||||
|
||||
# void line
|
||||
row = self.selected_item.data['row']
|
||||
if not row.void:
|
||||
row = session.get(row.__class__, row.uuid)
|
||||
handler.void_row(row, user)
|
||||
self.selected_item.data['row'] = row
|
||||
self.selected_item.content.mark_void()
|
||||
self.selected_item.bgcolor = 'white'
|
||||
self.selected_item = None
|
||||
|
||||
# update screen to reflect new balance
|
||||
self.txn_total.value = self.app.render_currency(batch.get_balance())
|
||||
|
||||
|
||||
else:
|
||||
self.page.snack_bar = ft.SnackBar(ft.Text("LINE ALREADY VOID",
|
||||
color='black',
|
||||
size=20,
|
||||
weight=ft.FontWeight.BOLD),
|
||||
bgcolor='yellow',
|
||||
duration=1500)
|
||||
self.page.snack_bar.open = True
|
||||
|
||||
|
||||
else:
|
||||
# void txn
|
||||
handler.void_batch(batch, user)
|
||||
self.clear_all()
|
||||
|
||||
session.commit()
|
||||
session.close()
|
||||
|
||||
self.clear_all()
|
||||
self.reset()
|
||||
|
||||
def cancel(e):
|
||||
|
@ -942,11 +996,13 @@ class POSView(WuttaView):
|
|||
|
||||
if batch:
|
||||
|
||||
# prompt to void something
|
||||
target = 'LINE' if self.selected_item else 'TXN'
|
||||
dlg = ft.AlertDialog(
|
||||
title=ft.Text("Confirm VOID"),
|
||||
content=ft.Text("Really VOID this transaction?"),
|
||||
content=ft.Text(f"Really VOID {target}?"),
|
||||
actions=[
|
||||
ft.Container(content=ft.Text("Yes, VOID",
|
||||
ft.Container(content=ft.Text(f"VOID {target}",
|
||||
size=self.default_font_size,
|
||||
color='black',
|
||||
weight=ft.FontWeight.BOLD),
|
||||
|
|
Loading…
Reference in a new issue