import conf import sqlalchemy from db import User, Purchase, PurchaseEntry, Transaction, Product from text_interface.helpermenus import Menu class BuyMenu(Menu): def __init__(self, session=None): Menu.__init__(self, 'Buy', uses_db=True) if session: self.session = session self.superfast_mode = False self.help_text = ''' Each purchase may contain one or more products and one or more buyers. Enter products (by name or bar code) and buyers (by name or bar code) in any order. The information gathered so far is displayed after each addition, and you can type 'what' at any time to redisplay it. When finished, write an empty line to confirm the purchase.\n''' @staticmethod def credit_check(user): """ :param user: :type user: User :rtype: boolean """ assert isinstance(user, User) return user.credit > conf.low_credit_warning_limit def low_credit_warning(self, user, timeout=False): assert isinstance(user, User) print("***********************************************************************") print("***********************************************************************") print("") print("$$\ $$\ $$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ $$$$$$\\") print("$$ | $\ $$ |$$ __$$\ $$ __$$\ $$$\ $$ |\_$$ _|$$$\ $$ |$$ __$$\\") print("$$ |$$$\ $$ |$$ / $$ |$$ | $$ |$$$$\ $$ | $$ | $$$$\ $$ |$$ / \__|") print("$$ $$ $$\$$ |$$$$$$$$ |$$$$$$$ |$$ $$\$$ | $$ | $$ $$\$$ |$$ |$$$$\\") print("$$$$ _$$$$ |$$ __$$ |$$ __$$< $$ \$$$$ | $$ | $$ \$$$$ |$$ |\_$$ |") print("$$$ / \$$$ |$$ | $$ |$$ | $$ |$$ |\$$$ | $$ | $$ |\$$$ |$$ | $$ |") print("$$ / \$$ |$$ | $$ |$$ | $$ |$$ | \$$ |$$$$$$\ $$ | \$$ |\$$$$$$ |") print("\__/ \__|\__| \__|\__| \__|\__| \__|\______|\__| \__| \______/") print("") print("***********************************************************************") print("***********************************************************************") print("") print(f"USER {user.name} HAS LOWER CREDIT THAN {conf.low_credit_warning_limit:d}.") print("THIS PURCHASE WILL CHARGE YOUR CREDIT TWICE AS MUCH.") print("CONSIDER PUTTING MONEY IN THE BOX TO AVOID THIS.") print("") print("Do you want to continue with this purchase?") if timeout: print("THIS PURCHASE WILL AUTOMATICALLY BE PERFORMED IN 3 MINUTES!") return self.confirm(prompt=">", default=True, timeout=180) else: return self.confirm(prompt=">", default=True) def add_thing_to_purchase(self, thing, amount=1): if isinstance(thing, User): if thing.is_anonymous(): print('---------------------------------------------') print('| You are now purchasing as the user anonym.|') print('| You have to put money in the anonym-jar. |') print('---------------------------------------------') if not self.credit_check(thing): if self.low_credit_warning(user=thing, timeout=self.superfast_mode): Transaction(thing, purchase=self.purchase, penalty=2) else: return False else: Transaction(thing, purchase=self.purchase) elif isinstance(thing, Product): if self.purchase.entries: for entry in self.purchase.entries: if entry.product == thing: entry.amount += amount return True PurchaseEntry(self.purchase, thing, amount) return True def _execute(self, initial_contents=None): self.print_header() self.purchase = Purchase() self.exit_confirm_msg = None self.superfast_mode = False if initial_contents is None: initial_contents = [] for thing, num in initial_contents: self.add_thing_to_purchase(thing, num) def is_product(candidate): return isinstance(candidate[0], Product) if len(initial_contents) > 0 and all(map(is_product, initial_contents)): self.superfast_mode = True print('***********************************************') print('****** Buy menu is in SUPERFASTmode[tm]! ******') print('*** The purchase will be stored immediately ***') print('*** when you enter a user. ***') print('***********************************************') while True: self.print_purchase() self.printc({(False, False): 'Enter user or product identification', (False, True): 'Enter user identification or more products', (True, False): 'Enter product identification or more users', (True, True): 'Enter more products or users, or an empty line to confirm' }[(len(self.purchase.transactions) > 0, len(self.purchase.entries) > 0)]) # Read in a 'thing' (product or user): line = self.input_multiple(add_nonexisting=('user', 'product'), empty_input_permitted=True, find_hidden_products=False) if line is not None: thing, num = line else: thing, num = None, 0 # Possibly exit from the menu: if thing is None: if not self.complete_input(): if self.confirm('Not enough information entered. Abort purchase?', default=True): return False continue break else: # once we get something in the # purchase, we want to protect the # user from accidentally killing it self.exit_confirm_msg = 'Abort purchase?' # Add the thing to our purchase object: if not self.add_thing_to_purchase(thing, amount=num): continue # In super-fast mode, we complete the purchase once we get a user: if self.superfast_mode and isinstance(thing, User): break self.purchase.perform_purchase() self.session.add(self.purchase) try: self.session.commit() except sqlalchemy.exc.SQLAlchemyError as e: print(f'Could not store purchase: {e}') else: print('Purchase stored.') self.print_purchase() for t in self.purchase.transactions: if not t.user.is_anonymous(): print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr") if t.user.credit < conf.low_credit_warning_limit: print(f'USER {t.user.name} HAS LOWER CREDIT THAN {conf.low_credit_warning_limit:d},', 'AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.') # Superfast mode skips a linebreak for some reason. if self.superfast_mode: print("") return True def complete_input(self): return self.purchase.is_complete() def format_purchase(self): self.purchase.set_price() transactions = self.purchase.transactions entries = self.purchase.entries if len(transactions) == 0 and len(entries) == 0: return None string = 'Purchase:' string += '\n buyers: ' if len(transactions) == 0: string += '(empty)' else: string += ', '.join( [t.user.name + ("*" if not self.credit_check(t.user) else "") for t in transactions]) string += '\n products: ' if len(entries) == 0: string += '(empty)' else: string += "\n " string += '\n '.join([f'{e.amount:d}x {e.product.name} ({e.product.price:d} kr)' for e in entries]) if len(transactions) > 1: string += f'\n price per person: {self.purchase.price_per_transaction():d} kr' if any(t.penalty > 1 for t in transactions): # TODO: Use penalty multiplier instead of 2 string += f' *({self.purchase.price_per_transaction() * 2:d} kr)' string += f'\n total price: {self.purchase.price:d} kr' if any(t.penalty > 1 for t in transactions): total = sum(self.purchase.price_per_transaction() * t.penalty for t in transactions) string += f'\n *total with penalty: {total} kr' return string def print_purchase(self): info = self.format_purchase() if info is not None: self.set_context(info)