Add button for "Adjust Price"
so far no constraints: any user, any item, any price
This commit is contained in:
		
							parent
							
								
									4ed006a93f
								
							
						
					
					
						commit
						9340e0d1bc
					
				
					 4 changed files with 234 additions and 52 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar