Example #1
0
def most_bought_of_one_product_by_groups_from_group(drinkid, groupid,
                                                    parsedbegin, parsedend):
    count = {}
    purchases = Purchase.query.filter(
        and_(Purchase.product_id == drinkid, Purchase.timestamp >= parsedbegin,
             Purchase.timestamp <= parsedend, Purchase.round == False)).all()

    for pur in purchases:
        if User.query.get(pur.user_id).usergroup_id == groupid:
            if pur.user_id not in count:
                count[pur.user_id] = pur.amount
            else:
                count[pur.user_id] = count[pur.user_id] + pur.amount

    count_groups = {}
    for u_id, amount in count.items():
        group = User.query.get(u_id).usergroup_id
        if group not in count_groups:
            count_groups[group] = amount
        else:
            count_groups[group] = count_groups[group] + amount

    datag = []
    for g_id, amount in count_groups.items():
        datag.append(
            (g_id, Usergroup.query.get(g_id).name, round_down(amount)))

    idsg, valuesg, labelsg = top_n(count_groups, datag, 20)

    return idsg, valuesg, labelsg
Example #2
0
def borrel_mode(drink_id=None):
    global borrel_mode_enabled, borrel_mode_drinks

    # Get the user that is paying for everything in borrel mode
    borrel_mode_user = int(settings['borrel_mode_user'])
    # If the user is -1, borrel mode is disabled so we do not have to execute anything
    if borrel_mode_enabled:
        # If the current drink is in the products...
        if drink_id is None or drink_id in borrel_mode_drinks:
            total_bought = 0
            # We count the total amount of borrel mode products bought (also before the borrel started)
            for p1 in borrel_mode_drinks:
                for p in Purchase.query.filter(Purchase.user_id == borrel_mode_user, Purchase.product_id == p1,
                                               Purchase.price > 0).all():
                    total_bought += p.amount
            total_bought = round_up(total_bought)

            # Calculate how many products are left
            left_over = round_up(float(settings['borrel_mode_amount']), 2) + round_up(float(settings['borrel_mode_start_amount']), 2) - total_bought
            # If it is zero or less...
            if left_over <= 0:
                # We disable borrel mode by changing some settings and variables
                update_settings('borrel_mode_user', -1)
                borrel_mode_enabled = False
                return None

            # If borrel mode is on, we set the boolean and return how many is left and who is paying
            borrel_mode_enabled = True
            return {'left': round_down(left_over, 0),
                    'user': User.query.get(borrel_mode_user).name}

    return None
Example #3
0
def most_bought_products_by_users(parsedbegin, parsedend):
    count = {}
    purchases = Purchase.query.filter(
        and_(Purchase.timestamp >= parsedbegin,
             Purchase.timestamp <= parsedend,
             Purchase.product_id != settings['dinner_product_id'],
             Purchase.round == False)).all()
    for pur in purchases:
        if pur.product_id not in count:
            count[pur.product_id] = pur.amount
        else:
            count[pur.product_id] = count[pur.product_id] + pur.amount

    datap = []
    for p_id, amount in count.items():
        datap.append((p_id, Product.query.get(p_id).name, round_down(amount)))

    return topall(count, datap)
Example #4
0
def most_bought_of_one_product_by_users(drinkid, parsedbegin, parsedend):
    count = {}
    purchases = Purchase.query.filter(
        and_(Purchase.product_id == drinkid, Purchase.timestamp >= parsedbegin,
             Purchase.timestamp <= parsedend, Purchase.round == False)).all()

    for pur in purchases:
        if pur.user_id not in count:
            count[pur.user_id] = pur.amount
        else:
            count[pur.user_id] = count[pur.user_id] + pur.amount

    datau = []
    for u_id, amount in count.items():
        datau.append((u_id, User.query.get(u_id).name, round_down(amount)))
    idsu, valuesu, labelsu = top_n(count, datau, 20)

    return idsu, valuesu, labelsu
Example #5
0
def most_bought_products_per_user(userid, parsedbegin, parsedend, n=20):
    count = {}

    purchases = Purchase.query.filter(
        and_(Purchase.user_id == userid, Purchase.timestamp >= parsedbegin,
             Purchase.timestamp <= parsedend, Purchase.round == False,
             Purchase.product_id != settings['dinner_product_id'])).all()

    for p in purchases:
        if p.product_id not in count:
            count[p.product_id] = p.amount
        else:
            count[p.product_id] = count[p.product_id] + p.amount

    data = []
    for p_id, amount in count.items():
        data.append((p_id, Product.query.get(p_id).name, round_down(amount)))
    ids, values, labels = top_n(count, data, n)

    return ids, values, labels
Example #6
0
def addpurchase(drink_id, user_id, quantity, rondje, price_per_one):
    # Round quantity to at most two decimals
    quantity = round_up(quantity)
    # Get drink and user objects from database
    drink = Product.query.get(drink_id)
    user = User.query.get(user_id)

    # If the price is zero, we do not have to take any inventory
    if price_per_one > 0:
        # If we product is not a mix, we can simply take it from inventory
        if drink.recipe_input is None:
            inventory = take_from_inventory(user, drink_id, quantity)
        # However, if the product is a mix...
        else:
            inventory = {'costs': 0, 'inventory_usage': []}
            # For every ingredient of the mix...
            for r in Recipe.query.filter(Recipe.product_id == drink.id).all():
                # Take the respective quantity from inventory
                result = take_from_inventory(user, r.ingredient_id, round_up(r.quantity * quantity))
                # Add the costs of the ingredient to the total costs
                inventory['costs'] = inventory['costs'] + result['costs']
                # Add the inventory usage
                inventory['inventory_usage'] = inventory['inventory_usage'] + result['inventory_usage']
    else:
        # We take no inventory if the price is zero, so the costs are zero
        inventory = {'costs': 0, 'inventory_usage': []}

    # Get the profitgroup of the user
    profitgroup = Usergroup.query.get(user.profitgroup_id)
    # Calculate the profit
    prof = round_down((price_per_one * quantity - inventory['costs']) * app.config['PROFIT_PERCENTAGE'])
    # Add the profit to the group
    profitgroup.profit = profitgroup.profit + prof
    # Create a row object for the profit table
    profit_obj = Profit(profitgroup_id=profitgroup.id, timestamp=datetime.now(),
                        percentage=app.config['PROFIT_PERCENTAGE'], change=prof, new=profitgroup.profit)
    db.session.add(profit_obj)
    # Save to database
    db.session.commit()

    # Calculate the new user balance
    user.balance = user.balance - round_up(float(price_per_one) * quantity)
    # Create a purchase entry in the table, so we can use its purchase ID to create the transaction
    purchase = Purchase(user_id=user.id, timestamp=datetime.now(), product_id=drink.id, price=price_per_one,
                        amount=quantity, round=rondje)
    db.session.add(purchase)
    db.session.commit()

    # Create all inventory usage entries
    for x in inventory['inventory_usage']:
        i_u = Inventory_usage(purchase_id=purchase.id, inventory_id=x['id'], quantity=x['quantity'])
        db.session.add(i_u)
        db.session.commit()
        # Set the object to None to clean up after creation
        i_u = None

    # Calculate the change in balance
    balchange = round_down(-price_per_one * quantity)
    # Create a transaction entry and add it to the database
    transaction = Transaction(user_id=user.id, timestamp=datetime.now(), purchase_id=purchase.id,
                              profit_id=profit_obj.id, balchange=balchange, newbal=user.balance)
    db.session.add(transaction)
    db.session.commit()

    # Update the daily stats with the new purchase
    statshandler.update_daily_stats('euros', balchange)
    statshandler.update_daily_stats_purchase(user_id, drink_id, quantity, rondje, price_per_one)

    return quantity, drink.name, user.name, "success"
Example #7
0
def correct_inventory(json):
    # Create a list of all groups that will participate in this inventory correction
    all_groups = set()
    for i in json:
        s = set(i['groups'])
        all_groups.update(s)
    all_groups = list(all_groups)
    per_group_costs = [0] * len(all_groups)
    total_costs = 0

    # Create a document for the overview of this correction
    now = datetime.now()
    document = Document()
    document.add_heading('Inventaris Correctie', 0)

    document.add_paragraph("Op {} om {} is er een inventariscorrectie uitgevoerd. Hierbij zijn de volgende wijzigingen "
                           "doorgevoerd in de inventaris.".format(now.strftime("%Y-%m-%d"), now.strftime("%H:%M:%S")))

    # Table layout: Product name | Inventory in Tikker | Real inventory | Difference | Costs | <per group its costs or
    # profit
    table = document.add_table(rows=1, cols=5 + len(all_groups))
    header_cells = table.rows[0].cells
    header_cells[0].text = "Productnaam"
    header_cells[1].text = "Inv in Tikker"
    header_cells[2].text = "Echte inv"
    header_cells[3].text = "Verschil"
    header_cells[4].text = "Kosten"
    for i in range(0, len(all_groups)):
        header_cells[5 + i].text = "Kosten {}".format(Usergroup.query.get(all_groups[i]).name)

    for i in json:
        # Get the product object
        p = Product.query.get(i['product_id'])
        # Calculate the inventory difference
        tikker_inv = calcStock(p.id)
        diff = int(i['stock']) - int(tikker_inv)

        # Create a new row for the document table
        row_cells = table.add_row().cells
        row_cells[0].text = p.name
        row_cells[1].text = str(tikker_inv)
        row_cells[2].text = str(i['stock'])
        row_cells[3].text = str(diff)
        if diff < 0:
            row_cells[4].text = '-€ %.2f' % round_up(-diff * p.price)
            total_costs += round_up(-diff * p.price)
        elif diff > 0:
            row_cells[4].text = '€ %.2f' % round_down(diff * p.price)
            total_costs += round_down(diff * p.price)

        # If the difference is less than 0, we have lost some inventory, which costs money
        if diff < 0:
            take_from_inventory(None, p.id, -diff)
            for g_id in i['groups']:
                # Get the group object
                g = Usergroup.query.get(g_id)
                # Calculate the costs for this group
                cost = round_down(diff * p.price / len(i['groups']))
                # Change its profit
                g.profit = g.profit + cost
                profit = Profit(profitgroup_id=g_id, timestamp=datetime.now(), percentage=1.0,
                                change=cost, new=g.profit, description="{} {} inventariscorrectie".format(str(diff), p.name))
                db.session.add(profit)
                db.session.commit()
                # Now, take it from inventory
                # Add it to the row in the table and to the cumulative costs
                index = all_groups.index(g_id)
                per_group_costs[index] += cost
                row_cells[5 + index].text = '-€ %.2f' % -cost
                # Reset the group object
                g = None
        elif diff > 0:
            new_price = find_newest_product_price(p.id)
            add_inventory(p.id, diff, new_price, "Inventariscorrectie")
            for g_id in i['groups']:
                g = Usergroup.query.get(g_id)
                profit_for_group = round_down(diff * new_price / len(i['groups']))
                g.profit = g.profit + profit_for_group
                profit = Profit(profitgroup_id=g_id, timestamp=datetime.now(), percentage=1.0,
                                change=profit_for_group, new=g.profit,
                                description="{} {} inventariscorrectie".format(str(diff), p.name))
                db.session.add(profit)
                db.session.commit()
                # Add it to the row in the table and to the cumulative costs
                index = all_groups.index(g_id)
                per_group_costs[index] += profit_for_group
                row_cells[5 + index].text = '€ %.2f' % profit_for_group
                # Reset the group object
                g = None

    row_cells = table.add_row().cells
    if total_costs < 0:
        row_cells[4].text = '-€ %.2f' % -total_costs
    else:
        row_cells[4].text = '€ %.2f' % total_costs

    for i in range(0, len(per_group_costs)):
        if per_group_costs[i] < 0:
            row_cells[5 + i].text = '-€ %.2f' % -per_group_costs[i]
        else:
            row_cells[5 + i].text = '€ %.2f' % per_group_costs[i]

    # Try to save the file
    # If it is not possible because the file is locked, try it again with a different filename until it works
    count = 0
    while True:
        filename = os.path.join(app.config['DOCUMENT_FOLDER'], 'inventariscorrectie_{}_{}.docx'
                                .format(now.strftime("%Y%m%d"), count))
        # If the file exists, raise the file name and try again
        if os.path.exists(filename):
            count += 1
            continue
        try:
            # If saving the file is successful, escape the loop and finish
            document.save(filename)
            break
        # If the file is opened, catch the error and try again
        except PermissionError:
            count += 1

    return "Inventaris correctie succesvol doorgevoerd! Het rapport is opgeslagen in {}".format(filename), "success"
Example #8
0
def admin_treasurer():
    check_if_local_machine()

    filters_inv = InventoryFilterForm()
    filters_users = UsersFilterForm()

    now = datetime.now()
    three_months_ago = now - timedelta(days=7 * 12)
    '''
    Query the data from the database and calculate other necessary values
    '''

    # Query the inventory quantity left for each product
    rawquery = db.session.query(Product.name, Product.id, Product.category, Product.inventory_warning,
                                func.sum(Inventory.quantity.cast(Integer)).label('quantity'),
                                func.sum(Inventory.quantity * Inventory.price_before_profit)
                                .label('inventory_value'))\
        .filter(and_(Product.id == Inventory.product_id, Product.purchaseable == True,
                     Product.id != dbhandler.settings['dinner_product_id']))\
        .group_by(Inventory.product_id)

    # Apply the filters to the query
    if 'f_product_category' in request.args and request.args.get(
            'f_product_category') != 'all':
        rawquery = rawquery.filter(
            Product.category == request.args.get('f_product_category'))
    # Convert the result from a list of tuples to a list of dicts
    products = [p._asdict() for p in rawquery.all()]

    # Calculate the total value of the inventory
    total_p_value = sum([p['inventory_value'] for p in products])

    # Query the consumption of each product per week over a period of 12 weeks
    purchases = db.session.query(Purchase.product_id.label('id'),
                                 func.sum(Purchase.amount / 12).label('per_week'))\
        .filter(Purchase.round == False, Purchase.timestamp > three_months_ago)\
        .group_by(Purchase.product_id).all()
    # Convert the result to a list of dicts
    purchases = [p._asdict() for p in purchases]

    # Function to calculate when the stock will be empty
    def stock_empty(p, source):
        return datetime.now() + timedelta(days=int(p['quantity'] /
                                                   p['per_week'] * 7))

    # Merge the products and purchases table
    products = merge_queries(products, purchases, ['per_week', 'stock_empty'],
                             [0, None], [None, None], [None, stock_empty])
    '''
    Create the analysis graphs
    '''

    # Create two empty lists for parsed data for the graphs
    qdata, vdata = [], []
    # For each product...
    for p in products:
        # Add a tuple to the lists
        qdata.append((p['id'], p['name'], round(p['quantity'])))
        vdata.append((p['id'], p['name'], round_down(p['inventory_value'])))
    # Process the data lists for the graphs on the page
    product_q, product_v = {}, {}
    product_q['ids'], product_q['value'], product_q[
        'labels'] = statshandler.topall(None, qdata)
    product_v['ids'], product_v['value'], product_v[
        'labels'] = statshandler.topall(None, vdata)
    '''
    Create the category graphs 
    '''

    # Create two empty lists for parsed data for the graphs
    qdata, vdata = [], []
    # Create a dictionary to store all the cumulative data
    categories = {}
    # Loop over all products
    for p in products:
        # If a product category is not yet in the dictionary...
        if p['category'] not in categories:
            # Add it with the corresponding values
            categories[p['category']] = {
                'quantity': p['quantity'],
                'value': p['inventory_value']
            }
        # If is is, increment the values in the dictionary
        else:
            categories[p['category']]['quantity'] += p['quantity']
            categories[p['category']]['value'] += p['inventory_value']

    # Remove the empty category if it is present
    if "" in categories:
        categories.pop("")

    # Add all the data to the two lists
    for category, v in categories.items():
        qdata.append((0, category, round(v['quantity'])))
        vdata.append((0, category, round(v['value'])))
    # Parse the data into readable data for the graphs
    category_q, category_v = {}, {}
    category_q['ids'], category_q['value'], category_q[
        'labels'] = statshandler.topall(None, qdata)
    category_v['ids'], category_v['value'], category_v[
        'labels'] = statshandler.topall(None, vdata)
    '''
    Query the users table
    '''

    # The base query for all the users
    users_query = db.session.query(User.id, User.name, User.balance, Usergroup.name.label('group'))\
        .filter(User.usergroup_id == Usergroup.id)\
        .order_by(User.usergroup_id)

    # Stolen from the function above, because the filter function cannot be called.
    # There are two tables on this page and applying filters on both causes problems
    if 'f_user_usergroup' in request.args and int(
            request.args.get('f_user_usergroup')) > 0:
        users_query = users_query.filter(
            User.usergroup_id == int(request.args.get('f_user_usergroup')))
    if 'f_user_profitgroup' in request.args and int(
            request.args.get('f_user_profitgroup')) > 0:
        users_query = users_query.filter(
            User.profitgroup_id == int(request.args.get('f_user_profitgroup')))
    # Finish the query and convert all items to dictionaries
    users = [u._asdict() for u in users_query.all()]

    #  Query the amount every user spends per week (to merge later)
    purchase1 = db.session.query(Purchase.user_id.label('id'),
                                 func.sum(Purchase.amount * Purchase.price / 12).label('per_week'))\
        .filter(Purchase.timestamp > three_months_ago).group_by(Purchase.user_id).all()
    # Convert it to a dictionary
    purchase1 = [p._asdict() for p in purchase1]

    # Query the average balance of every user during three months
    transaction1 = db.session.query(Transaction.user_id.label('id'),
                                    func.avg(Transaction.newbal).label('average_balance'))\
        .filter(Transaction.timestamp > three_months_ago).group_by(Transaction.user_id).all()
    # Convert it to a dictionary
    transaction1 = [t._asdict() for t in transaction1]

    # Query the last time the user had a positive balance (earliest from now on)
    transaction2 = db.session.query(Transaction.user_id.label('id'), Transaction.timestamp.label('last_positive'))\
        .filter(Transaction.newbal > 0).group_by(Transaction.user_id).all()
    #  Convert it to a dictionary
    transaction2 = [t._asdict() for t in transaction2]

    # Condition that will be used later when merging
    def positive_balance(user):
        if user['balance'] > 0:
            return True
        return False

    # Function to calculate the (expected) time until the user has no more balance
    def no_balance_left(u, source):
        return datetime.now() + timedelta(days=int(u['balance'] /
                                                   u['per_week'] * 7))

    # Merge all the three seperate queries with the users query
    users = merge_queries(users, purchase1, ['per_week', 'no_balance_left'],
                          [0, None], [None, positive_balance],
                          [None, no_balance_left])
    users = merge_queries(users, transaction1, ['average_balance'], [None],
                          [None], [None])
    users = merge_queries(users, transaction2, ['last_positive'],
                          [datetime.now()], [None], [None])

    # Because not every user has an average balance and because it is hard to parameterize
    # in the merge_queries function above, we add this average balance manually
    for u in users:
        if u['average_balance'] is None:
            u['average_balance'] = u['balance']

    # Calculate the sum of all the balances and other numbers
    total_u_balance = sum([u['balance'] for u in users])
    total_u_average = sum([u['average_balance'] for u in users])
    total_u_per_week = sum([u['per_week'] for u in users])
    '''
    Finalize
    '''

    # Calculate the render time
    render_time = int((datetime.now() - now).microseconds / 1000)

    return render_template('admin/treasurer.html',
                           title='Penno Panel',
                           h1='Penno Panel',
                           products=products,
                           filters_inv=filters_inv,
                           filters_users=filters_users,
                           total_p_value=total_p_value,
                           product_q=product_q,
                           product_v=product_v,
                           category_q=category_q,
                           category_v=category_v,
                           rendertime=render_time,
                           users=users,
                           total_u_balance=total_u_balance,
                           total_u_average=total_u_average,
                           total_u_per_week=total_u_per_week), 200