|
@@ -4,16 +4,31 @@
|
|
|
# All rights reserved
|
|
|
#
|
|
|
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
|
|
|
-from collections import OrderedDict
|
|
|
+import itertools
|
|
|
from typing import Callable, List, Union
|
|
|
-
|
|
|
-import urwid
|
|
|
+from urwid import (
|
|
|
+ connect_signal,
|
|
|
+ AttrMap,
|
|
|
+ Button,
|
|
|
+ Columns,
|
|
|
+ Divider,
|
|
|
+ Filler,
|
|
|
+ LineBox,
|
|
|
+ Padding,
|
|
|
+ Pile,
|
|
|
+ Text,
|
|
|
+)
|
|
|
from app.activities import ActivityManager
|
|
|
from app.db_utils import QueryManager
|
|
|
|
|
|
from .. import COPYRIGHT
|
|
|
-from ..widgets import (AutoCompleteEdit, AutoCompleteFloatEdit,
|
|
|
- AutoCompletePopUp, FocusWidget, NoTabCheckBox)
|
|
|
+from ..widgets import (
|
|
|
+ AutoCompleteEdit,
|
|
|
+ AutoCompleteFloatEdit,
|
|
|
+ FocusWidget,
|
|
|
+ AutoCompletePopUp,
|
|
|
+ NoTabCheckBox
|
|
|
+)
|
|
|
|
|
|
|
|
|
class TransactionEditor(FocusWidget):
|
|
@@ -39,12 +54,6 @@ class TransactionEditor(FocusWidget):
|
|
|
else:
|
|
|
return super().keypress(size, key)
|
|
|
|
|
|
- def _init_data(self, fields: List[str]) -> None:
|
|
|
- self.data = OrderedDict([
|
|
|
- (k, '') for k in fields
|
|
|
- ])
|
|
|
- self.clear()
|
|
|
-
|
|
|
def apply_choice(self, name, value):
|
|
|
self.apply_changes(name, value)
|
|
|
for k,v in self.data.items():
|
|
@@ -55,35 +64,50 @@ class TransactionEditor(FocusWidget):
|
|
|
self.apply_changes(k, list(options)[0])
|
|
|
|
|
|
def apply_changes(self, name, value):
|
|
|
- self.data.update({
|
|
|
+ self.data = {
|
|
|
name: value if name != 'organic' else {
|
|
|
'yes': True, 'no': False,
|
|
|
True: True, False: False,
|
|
|
'mixed': '',
|
|
|
}[value],
|
|
|
- })
|
|
|
-
|
|
|
- def apply_organic_state(self, w, state):
|
|
|
- self.data['organic'] = state if state != 'mixed' else ''
|
|
|
+ }
|
|
|
+
|
|
|
+ @property
|
|
|
+ def data(self):
|
|
|
+ ret = dict(itertools.chain(
|
|
|
+ [(k, v.get_edit_text()) for k,v in self.edit_fields.items()],
|
|
|
+ [(k, v.state) for k,v in self.checkboxes.items()]
|
|
|
+ ))
|
|
|
+ return ret
|
|
|
+
|
|
|
+ @data.setter
|
|
|
+ def data(self, _data: dict):
|
|
|
+ for k,v in _data.items():
|
|
|
+ if k in self.edit_fields and v != self.edit_fields[k].get_edit_text():
|
|
|
+ self.edit_fields[k].set_edit_text(v)
|
|
|
+ if k in self.checkboxes and v != self.checkboxes[k].state:
|
|
|
+ self.checkboxes[k].set_state(v)
|
|
|
|
|
|
def clear(self):
|
|
|
- for k in self.data:
|
|
|
+ for (k, ef) in self.edit_fields.items():
|
|
|
if k in ('ts', 'store',):
|
|
|
continue
|
|
|
- self.data[k] = ''
|
|
|
-
|
|
|
+ ef.set_edit_text('')
|
|
|
+ for (_, cb) in self.checkboxes.items():
|
|
|
+ cb.set_state('mixed')
|
|
|
+ for (k, tf) in self.text_fields.items():
|
|
|
+ if k != 'dbview':
|
|
|
+ tf.set_text('')
|
|
|
return self.update()
|
|
|
|
|
|
def update(self):
|
|
|
- for k in self.edit_fields:
|
|
|
- self.edit_fields[k].set_edit_text(self.data[k])
|
|
|
- date, store = self.data['ts'], self.data['store']
|
|
|
+ data = self.data
|
|
|
+ date, store = data['ts'], data['store']
|
|
|
self.text_fields['dbview'].set_text(
|
|
|
self.query_manager.get_session_transactions(date, store) if None not in (
|
|
|
date or None, store or None
|
|
|
) else ''
|
|
|
)
|
|
|
- self.organic_checkbox.set_state(self.data['organic'] if self.data['organic'] != '' else 'mixed')
|
|
|
return self
|
|
|
|
|
|
def focus_on_product(self):
|
|
@@ -95,71 +119,90 @@ class TransactionEditor(FocusWidget):
|
|
|
def __init__(self,
|
|
|
activity_manager: ActivityManager,
|
|
|
query_manager: QueryManager,
|
|
|
- fields: List[str],
|
|
|
- layout, side_pane, bottom_pane,
|
|
|
save_and_clear_cb,
|
|
|
autocomplete_cb: Callable[[
|
|
|
Union[AutoCompleteEdit, AutoCompleteFloatEdit], str, dict
|
|
|
], None],
|
|
|
):
|
|
|
- self.organic_checkbox = NoTabCheckBox(
|
|
|
- u"Organic",
|
|
|
- on_state_change=self.apply_organic_state
|
|
|
- )
|
|
|
self.query_manager = query_manager
|
|
|
self.save_and_clear_cb = save_and_clear_cb
|
|
|
- self.layout = layout
|
|
|
- self.side_pane = side_pane
|
|
|
- self.bottom_pane = bottom_pane
|
|
|
- self.edit_fields = OrderedDict()
|
|
|
- self.text_fields = OrderedDict()
|
|
|
+ self.buttons = {
|
|
|
+ 'done': Button(('streak', u'Done')),
|
|
|
+ 'clear': Button(('streak', u'Clear')),
|
|
|
+ }
|
|
|
+ self.edit_fields = {
|
|
|
+ 'ts': AutoCompleteEdit(('bg', 'ts')),
|
|
|
+ 'store': AutoCompleteEdit(('bg', 'store')),
|
|
|
+ 'product': AutoCompleteEdit(('bg', 'product')),
|
|
|
+ 'category': AutoCompleteEdit(('bg', 'category')),
|
|
|
+ 'group': AutoCompleteEdit(('bg', 'group')),
|
|
|
+ 'description': AutoCompleteEdit(('bg', 'description')),
|
|
|
+ 'unit': AutoCompleteEdit(('bg', 'unit')),
|
|
|
+ 'quantity': AutoCompleteFloatEdit(('bg', 'quantity')),
|
|
|
+ 'price': AutoCompleteFloatEdit(('bg', 'price')),
|
|
|
+ }
|
|
|
+ self.checkboxes = {
|
|
|
+ 'organic': NoTabCheckBox(('bg', "Organic"), state='mixed'),
|
|
|
+ }
|
|
|
+ self.text_fields = {
|
|
|
+ 'dbview': Text('')
|
|
|
+ }
|
|
|
+ self.organic_checkbox = self.checkboxes['organic']
|
|
|
+ connect_signal(self.organic_checkbox, 'postchange', lambda _,v: self.update())
|
|
|
+ layout = [
|
|
|
+ [ 'ts', 'store', ],
|
|
|
+ [ 'organic', 'product', ],
|
|
|
+ [ 'category', 'group', ],
|
|
|
+ ]
|
|
|
+ side_pane = [
|
|
|
+ 'unit',
|
|
|
+ 'quantity',
|
|
|
+ 'price',
|
|
|
+ ]
|
|
|
+ bottom_pane = [
|
|
|
+ 'description',
|
|
|
+ 'dbview',
|
|
|
+ ]
|
|
|
+ self.clear()
|
|
|
widgets = dict()
|
|
|
- txn_view = urwid.Text('')
|
|
|
- self.text_fields.update({'dbview': txn_view})
|
|
|
widgets.update({
|
|
|
- 'dbview': urwid.LineBox(
|
|
|
- urwid.AttrMap(txn_view, 'streak'),
|
|
|
+ 'dbview': LineBox(
|
|
|
+ AttrMap(self.text_fields['dbview'], 'streak'),
|
|
|
title="Session Data",
|
|
|
title_align='left',
|
|
|
)
|
|
|
})
|
|
|
- self._init_data(fields)
|
|
|
- for k in self.data:
|
|
|
- if k in self.side_pane and k != 'unit':
|
|
|
- ef = AutoCompleteFloatEdit(('bg', k))
|
|
|
- elif k != 'organic':
|
|
|
- ef = AutoCompleteEdit(('bg', k))
|
|
|
- else:
|
|
|
- continue
|
|
|
-
|
|
|
- ef.set_edit_text(self.data[k])
|
|
|
- urwid.connect_signal(ef, 'change', lambda w, v: self.apply_changes(w.name, v))
|
|
|
- urwid.connect_signal(ef, 'apply', lambda w, name: autocomplete_cb(w, name, self.data))
|
|
|
- self.edit_fields[k] = ef
|
|
|
+ for (k, ef) in self.edit_fields.items():
|
|
|
+ connect_signal(ef, 'postchange', lambda _,v: self.update())
|
|
|
+ connect_signal(ef, 'apply', lambda w, name: autocomplete_cb(w, name, self.data))
|
|
|
|
|
|
widgets.update(dict([
|
|
|
- (k, urwid.LineBox(
|
|
|
- urwid.AttrMap(AutoCompletePopUp(
|
|
|
+ (k, LineBox(
|
|
|
+ AttrMap(AutoCompletePopUp(
|
|
|
self.edit_fields[k],
|
|
|
self.apply_choice,
|
|
|
lambda: activity_manager.show(self.update())
|
|
|
), 'streak'), title=k.title(), title_align='left')
|
|
|
) for k in self.edit_fields if k != 'product'
|
|
|
]))
|
|
|
- header = urwid.Text(u'Fill Transaction', 'center')
|
|
|
- _copyright = urwid.Text(COPYRIGHT, 'center')
|
|
|
- done_button = urwid.Button(('streak', u'Done'))
|
|
|
- clear_button = urwid.Button(('streak', u'Clear'))
|
|
|
- urwid.connect_signal(done_button, 'click', lambda _: save_and_clear_cb())
|
|
|
- urwid.connect_signal(clear_button, 'click', lambda _: self.clear())
|
|
|
- banner = urwid.Pile([
|
|
|
- urwid.Padding(header, 'center', width=('relative', 100)),
|
|
|
- urwid.Padding(_copyright, 'center', width=('relative', 100)),
|
|
|
+ header = Text(u'Fill Transaction', 'center')
|
|
|
+ _copyright = Text(COPYRIGHT, 'center')
|
|
|
+
|
|
|
+ components = dict()
|
|
|
+ components['bottom_pane'] = Columns(
|
|
|
+ [(8, self.buttons['done']), Divider(), (9, self.buttons['clear'])]
|
|
|
+ )
|
|
|
+ connect_signal(self.buttons['done'], 'click', lambda _: save_and_clear_cb())
|
|
|
+ connect_signal(self.buttons['clear'], 'click', lambda _: self.clear())
|
|
|
+
|
|
|
+ banner = Pile([
|
|
|
+ Padding(header, 'center', width=('relative', 100)),
|
|
|
+ Padding(_copyright, 'center', width=('relative', 100)),
|
|
|
])
|
|
|
- banner = urwid.AttrMap(banner, 'banner')
|
|
|
+ banner = AttrMap(banner, 'banner')
|
|
|
widgets.update({
|
|
|
- 'product': urwid.LineBox(urwid.Columns([
|
|
|
- urwid.AttrMap(AutoCompletePopUp(
|
|
|
+ 'product': LineBox(Columns([
|
|
|
+ AttrMap(AutoCompletePopUp(
|
|
|
self.edit_fields['product'],
|
|
|
self.apply_choice,
|
|
|
lambda: activity_manager.show(self.update())
|
|
@@ -168,11 +211,11 @@ class TransactionEditor(FocusWidget):
|
|
|
], dividechars=2), title='Product', title_align='left')
|
|
|
})
|
|
|
|
|
|
- side_pane_widget = (12, urwid.Pile([
|
|
|
- widgets[r] if r is not None else urwid.Divider() for r in self.side_pane
|
|
|
+ side_pane_widget = (12, Pile([
|
|
|
+ widgets[r] if r is not None else Divider() for r in side_pane
|
|
|
]))
|
|
|
main_pane_widgets = []
|
|
|
- for i, r in enumerate(self.layout):
|
|
|
+ for i, r in enumerate(layout):
|
|
|
_widgets = []
|
|
|
for c in r:
|
|
|
if c is not None:
|
|
@@ -180,28 +223,21 @@ class TransactionEditor(FocusWidget):
|
|
|
continue
|
|
|
_widgets.append(widgets[c])
|
|
|
else:
|
|
|
- _widgets.append(urwid.Divider())
|
|
|
- main_pane_widgets.append(urwid.Columns(_widgets))
|
|
|
+ _widgets.append(Divider())
|
|
|
+ main_pane_widgets.append(Columns(_widgets))
|
|
|
|
|
|
-
|
|
|
- main_pane_widget = urwid.Pile(main_pane_widgets)
|
|
|
-
|
|
|
- widget = urwid.Pile([
|
|
|
+ widget = Pile([
|
|
|
banner,
|
|
|
- urwid.Divider(),
|
|
|
- urwid.Columns([
|
|
|
- main_pane_widget, side_pane_widget
|
|
|
- ],
|
|
|
- dividechars=2,
|
|
|
- ),
|
|
|
- *[ widgets[c] if c is not None else urwid.Divider() for c in self.bottom_pane ],
|
|
|
- urwid.Divider(),
|
|
|
- urwid.Columns([
|
|
|
- (8, done_button), urwid.Divider(), (9, clear_button)
|
|
|
- ])
|
|
|
+ Divider(),
|
|
|
+ Columns([
|
|
|
+ Pile(main_pane_widgets), side_pane_widget
|
|
|
+ ], dividechars=2),
|
|
|
+ *[ widgets[c] if c is not None else Divider() for c in bottom_pane ],
|
|
|
+ Divider(),
|
|
|
+ components['bottom_pane']
|
|
|
])
|
|
|
- widget = urwid.Filler(widget, 'top')
|
|
|
- widget = urwid.AttrMap(widget, 'bg')
|
|
|
+ widget = Filler(widget, 'top')
|
|
|
+ widget = AttrMap(widget, 'bg')
|
|
|
super().__init__(widget, 5, [2,0,0,0], [
|
|
|
[0, 0], [0,1],
|
|
|
[1,],
|