def test_balance_between_stocktakings_one_stocktaking(self): """ If only one stocktaking was made, no balance calculation can be done. This is checked with this test. """ # Insert the initial stocktaking. stocktakings = [{ 'product_id': 1, 'count': 10 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }] timestamp = datetime.strptime('2018-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') data = { 'stocktakings': stocktakings, 'timestamp': int(timestamp.timestamp()) } self.post(url='/stocktakingcollections', data=data, role='admin') # Query the first stocktaking start = StocktakingCollection.query.first() result = stocktaking_helpers._get_balance_between_stocktakings( start, None) self.assertEqual(result, None)
def get_balance_between_stocktakings(admin): """ Returns the balance between two stocktakingcollections. :param admin: Is the administrator user, determined by @adminRequired. :return: A dictionary containing all information about the balance between the stocktakings. """ allowed_params = {'start_id': int, 'end_id': int} args = check_allowed_parameters(allowed_params) start_id = args.get('start_id', None) end_id = args.get('end_id', None) # Check for all required arguments if not all([start_id, end_id]): raise exc.InvalidData() # Check the ids. if end_id <= start_id: raise exc.InvalidData() # Query the stocktakingcollections. start = StocktakingCollection.query.filter_by(id=start_id).first() end = StocktakingCollection.query.filter_by(id=end_id).first() # Return the balance. balance = _get_balance_between_stocktakings(start, end) return jsonify(balance), 200
def test_balance_between_stocktakings_with_profit_and_loss(self): """ This test checks whether the profits and losses and the resulting balance are calculated correctly. """ # Manipulate the product price timestamps ts = datetime.strptime('2017-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') for id in [1, 2, 3, 4]: ProductPrice.query.filter_by(id=id).first().timestamp = ts db.session.commit() # Insert the first stocktaking db.session.add(StocktakingCollection(admin_id=1)) db.session.flush() stocktakings = [{ 'product_id': 1, 'count': 100 }, { 'product_id': 2, 'count': 100 }, { 'product_id': 3, 'count': 100 }, { 'product_id': 4, 'count': 100 }] for s in stocktakings: db.session.add(Stocktaking(**s, collection_id=1)) # Manipulate first stocktaking timestamp # First stocktaking: 01.01.2018 ts = datetime.strptime('2018-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') StocktakingCollection.query.filter_by(id=1).first().timestamp = ts # Insert the second stocktaking db.session.add(StocktakingCollection(admin_id=1)) db.session.flush() stocktakings = [ { 'product_id': 1, 'count': 110 }, # Products have been added! { 'product_id': 2, 'count': 90 }, # Products have been lost! { 'product_id': 3, 'count': 100 }, { 'product_id': 4, 'count': 100 } ] for s in stocktakings: db.session.add(Stocktaking(**s, collection_id=2)) # Manipulate second stocktaking timestamp # Second stocktaking: 01.02.2018 ts = datetime.strptime('2018-02-01 09:00:00', '%Y-%m-%d %H:%M:%S') StocktakingCollection.query.filter_by(id=2).first().timestamp = ts # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=2).first() result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 0) self.assertEqual(products[2]['purchase_count'], 0) self.assertEqual(products[3]['purchase_count'], 0) self.assertEqual(products[4]['purchase_count'], 0) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 0) self.assertEqual(products[2]['purchase_sum_price'], 0) self.assertEqual(products[3]['purchase_sum_price'], 0) self.assertEqual(products[4]['purchase_sum_price'], 0) # Check replenish count self.assertEqual(products[1]['replenish_count'], 0) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 0) self.assertEqual(products[4]['replenish_count'], 0) # Check differences self.assertEqual(products[1]['difference'], 10) self.assertEqual(products[2]['difference'], -10) self.assertEqual(products[3]['difference'], 0) self.assertEqual(products[4]['difference'], 0) # Check balance self.assertEqual(products[1]['balance'], 10 * 300) self.assertEqual(products[2]['balance'], -10 * 50) self.assertEqual(products[3]['balance'], 0) self.assertEqual(products[4]['balance'], 0) # Check overall balance self.assertEqual(result['balance'], 2500) self.assertEqual(result['loss'], 500) self.assertEqual(result['profit'], 3000)
def test_balance_between_stocktakings_new_product_and_set_inactive(self): """ If a product was created between the first and the last stocktaking, which was then reset to inactive, the calculation of the balance must still be carried out correctly. This functionality is checked with this test. """ # Since the start of this test is the same as the previous one, it # can be run again to generate the required data. self.test_balance_between_stocktakings_product_creation() # Set the product 5 inactive with another stocktaking. stocktakings = [{ 'product_id': 1, 'count': 100 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }, { 'product_id': 5, 'count': 0 }] t = datetime.strptime('2018-05-01 09:00:00', '%Y-%m-%d %H:%M:%S') data = {'stocktakings': stocktakings, 'timestamp': int(t.timestamp())} self.post(url='/stocktakingcollections', data=data, role='admin') # Insert the last stocktaking. (Product 5 is not included) stocktakings = [{ 'product_id': 1, 'count': 100 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }] t = datetime.strptime('2018-06-02 09:00:00', '%Y-%m-%d %H:%M:%S') data = {'stocktakings': stocktakings, 'timestamp': int(t.timestamp())} self.post(url='/stocktakingcollections', data=data, role='admin') # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=4).first() self.assertFalse(5 in [s.product_id for s in start.stocktakings]) self.assertFalse(5 in [s.product_id for s in end.stocktakings]) result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4, 5}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 0) self.assertEqual(products[2]['purchase_count'], 0) self.assertEqual(products[3]['purchase_count'], 0) self.assertEqual(products[4]['purchase_count'], 0) self.assertEqual(products[5]['purchase_count'], 0) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 0) self.assertEqual(products[2]['purchase_sum_price'], 0) self.assertEqual(products[3]['purchase_sum_price'], 0) self.assertEqual(products[4]['purchase_sum_price'], 0) self.assertEqual(products[5]['purchase_sum_price'], 0) # Check replenish count self.assertEqual(products[1]['replenish_count'], 0) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 0) self.assertEqual(products[4]['replenish_count'], 0) self.assertEqual(products[5]['replenish_count'], 20) # Check differences self.assertEqual(products[1]['difference'], 0) self.assertEqual(products[2]['difference'], 0) self.assertEqual(products[3]['difference'], 0) self.assertEqual(products[4]['difference'], 0) self.assertEqual(products[5]['difference'], -20) # Check balance self.assertEqual(products[1]['balance'], 0) self.assertEqual(products[2]['balance'], 0) self.assertEqual(products[3]['balance'], 0) self.assertEqual(products[4]['balance'], 0) self.assertEqual(products[5]['balance'], -20 * 100) # Check overall balance self.assertEqual(result['balance'], -2000) self.assertEqual(result['loss'], 2000) self.assertEqual(result['profit'], 0)
def test_balance_between_stocktakings_product_creation(self): """ This test checks whether the calculation of the balance works correctly if a new product has been added since the first stocktaking. """ # Insert the first stocktaking db.session.add(StocktakingCollection(admin_id=1)) db.session.flush() stocktakings = [{ 'product_id': 1, 'count': 100 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }] for s in stocktakings: db.session.add(Stocktaking(**s, collection_id=1)) # Manipulate first stocktaking timestamp # First stocktaking: 01.01.2018 ts = datetime.strptime('2018-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') StocktakingCollection.query.filter_by(id=1).first().timestamp = ts # Create a product. data = {'name': 'Bread', 'price': 100, 'tags': [1]} self.post(url='/products', role='admin', data=data) # Manipulate the product price timestamps ts = datetime.strptime('2017-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') for product_price_id in [1, 2, 3, 4, 5]: ProductPrice.query.filter_by( id=product_price_id).first().timestamp = ts db.session.commit() # Create replenishment. replenishments = [{'product_id': 5, 'amount': 20, 'total_price': 2000}] data = { 'replenishments': replenishments, 'comment': 'My test comment', 'timestamp': '2020-02-24 12:00:00Z', 'seller_id': 5 } self.post(url='/replenishmentcollections', data=data, role='admin') ts = datetime.strptime('2018-03-01 09:00:00', '%Y-%m-%d %H:%M:%S') ReplenishmentCollection.query.filter_by(id=1).first().timestamp = ts db.session.commit() # Insert the second stocktaking. stocktakings = [{ 'product_id': 1, 'count': 100 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }, { 'product_id': 5, 'count': 10 }] t = datetime.strptime('2018-04-01 09:00:00', '%Y-%m-%d %H:%M:%S') data = {'stocktakings': stocktakings, 'timestamp': int(t.timestamp())} self.post(url='/stocktakingcollections', data=data, role='admin') # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=2).first() self.assertFalse(5 in [s.product_id for s in start.stocktakings]) self.assertTrue(5 in [s.product_id for s in end.stocktakings]) result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4, 5}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 0) self.assertEqual(products[2]['purchase_count'], 0) self.assertEqual(products[3]['purchase_count'], 0) self.assertEqual(products[4]['purchase_count'], 0) self.assertEqual(products[5]['purchase_count'], 0) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 0) self.assertEqual(products[2]['purchase_sum_price'], 0) self.assertEqual(products[3]['purchase_sum_price'], 0) self.assertEqual(products[4]['purchase_sum_price'], 0) self.assertEqual(products[5]['purchase_sum_price'], 0) # Check replenish count self.assertEqual(products[1]['replenish_count'], 0) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 0) self.assertEqual(products[4]['replenish_count'], 0) self.assertEqual(products[5]['replenish_count'], 20) # Check differences self.assertEqual(products[1]['difference'], 0) self.assertEqual(products[2]['difference'], 0) self.assertEqual(products[3]['difference'], 0) self.assertEqual(products[4]['difference'], 0) self.assertEqual(products[5]['difference'], -10) # Check balance self.assertEqual(products[1]['balance'], 0) self.assertEqual(products[2]['balance'], 0) self.assertEqual(products[3]['balance'], 0) self.assertEqual(products[4]['balance'], 0) self.assertEqual(products[5]['balance'], -10 * 100) # Check overall balance self.assertEqual(result['balance'], -1000) self.assertEqual(result['profit'], 0)
def test_balance_between_stocktakings_two_stocktakings(self): """ This test checks whether the calculation of the balance works correctly between two stocktakings. """ # Insert a purchase which lies before the first stocktaking. t = datetime.strptime('2017-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add( Purchase(user_id=1, product_id=1, amount=100, timestamp=t)) # Manipulate the product price timestamps ts = datetime.strptime('2017-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') for product_price_id in [1, 2, 3, 4]: ProductPrice.query.filter_by( id=product_price_id).first().timestamp = ts db.session.commit() # Insert the default stocktaking collections. self.insert_default_stocktakingcollections() # Manipulate the stocktaking timestamps # First stocktaking: 01.01.2018 ts = datetime.strptime('2018-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') StocktakingCollection.query.filter_by(id=1).first().timestamp = ts # Second stocktaking: 01.03.2018 ts = datetime.strptime('2018-03-01 09:00:00', '%Y-%m-%d %H:%M:%S') StocktakingCollection.query.filter_by(id=2).first().timestamp = ts # Insert the default replenishment collections. self.insert_default_replenishmentcollections() # Manipulate the replenishment timestamps # First replenishment: 01.01.2017 (Before the first stocktaking!) ts = datetime.strptime('2017-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') ReplenishmentCollection.query.filter_by(id=1).first().timestamp = ts # Second replenishment: 05.02.2019 (Between the stocktakings) ts = datetime.strptime('2018-02-05 09:00:00', '%Y-%m-%d %H:%M:%S') ReplenishmentCollection.query.filter_by(id=2).first().timestamp = ts # Insert some purchases t = datetime.strptime('2018-02-01 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=1, product_id=1, amount=1, timestamp=t)) t = datetime.strptime('2018-02-02 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=1, product_id=2, amount=5, timestamp=t)) t = datetime.strptime('2018-02-03 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=2, product_id=3, amount=8, timestamp=t)) t = datetime.strptime('2018-02-04 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=3, product_id=1, amount=2, timestamp=t)) # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=2).first() # Timeline # Date Event # # 01.01.2017 Purchase: 100 x Product 1 # 01.01.2017 Replenishment 1 # (Both are not taken into account for the calculation) # ----------------------------------- # 01.01.2018 Stocktaking 1 # - Product 1: 100 # - Product 2: 50 # - Product 3: 25 # - Product 4: 33 # # 01.02.2018 Purchase: 1 x Product 1 # 02.02.2018 Purchase: 5 x Product 2 # 03.02.2018 Purchase: 8 x Product 3 # 04.02.2018 Purchase: 2 x Product 1 # 05.02.2018 Replenishment 2 # - Product 1: 10 # - Product 3: 5 # # ----------------------------------- # Balance: # Product 1: 100 (start) - 3 (purchase) + 10 (replenishment) = 107 # Product 2: 50 (start) - 5 (purchase) + 0 (replenishment) = 45 # Product 3: 25 (start) - 8 (purchase) + 5 (replenishment) = 22 # Product 4: 33 (start) - 0 (purchase) + 0 (replenishment) = 33 # ----------------------------------- # 01.03.2018 Stocktaking 2 # - Product 1: 50 # - Product 2: 25 # - Product 3: 12 # - Product 4: 3 # ----------------------------------- # Loss # Product 1: 107 - 50 = 57 # Product 2: 45 - 25 = 20 # Product 3: 22 - 12 = 10 # Product 4: 33 - 3 = 30 result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 3) self.assertEqual(products[2]['purchase_count'], 5) self.assertEqual(products[3]['purchase_count'], 8) self.assertEqual(products[4]['purchase_count'], 0) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 900) self.assertEqual(products[2]['purchase_sum_price'], 250) self.assertEqual(products[3]['purchase_sum_price'], 800) self.assertEqual(products[4]['purchase_sum_price'], 0) # Check replenish count self.assertEqual(products[1]['replenish_count'], 10) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 5) self.assertEqual(products[4]['replenish_count'], 0) # Check differences self.assertEqual(products[1]['difference'], -57) self.assertEqual(products[2]['difference'], -20) self.assertEqual(products[3]['difference'], -10) self.assertEqual(products[4]['difference'], -30) # Check balance self.assertEqual(products[1]['balance'], -57 * 300) self.assertEqual(products[2]['balance'], -20 * 50) self.assertEqual(products[3]['balance'], -10 * 100) self.assertEqual(products[4]['balance'], -30 * 200) # Check overall balance self.assertEqual(result['balance'], -25100) self.assertEqual(result['loss'], 25100) self.assertEqual(result['profit'], 0)
def test_balance_between_stocktakings_product_set_to_inactive(self): """ This test checks whether the calculation of the balance works correctly if a product has been set to inactive since the first stocktaking. """ # Manipulate the product price timestamps ts = datetime.strptime('2017-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') for product_price_id in [1, 2, 3, 4]: ProductPrice.query.filter_by( id=product_price_id).first().timestamp = ts db.session.commit() # Insert the first stocktaking db.session.add(StocktakingCollection(admin_id=1)) db.session.flush() stocktakings = [{ 'product_id': 1, 'count': 100 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }] for s in stocktakings: db.session.add(Stocktaking(**s, collection_id=1)) # Manipulate first stocktaking timestamp # First stocktaking: 01.01.2018 ts = datetime.strptime('2018-01-01 09:00:00', '%Y-%m-%d %H:%M:%S') StocktakingCollection.query.filter_by(id=1).first().timestamp = ts # Insert a purchase. t = datetime.strptime('2018-03-05 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add( Purchase(user_id=1, product_id=1, amount=90, timestamp=t)) # Insert the second stocktaking. stocktakings = [{ 'product_id': 1, 'count': 0 }, { 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }] t = datetime.strptime('2018-04-01 09:00:00', '%Y-%m-%d %H:%M:%S') data = {'stocktakings': stocktakings, 'timestamp': int(t.timestamp())} self.post(url='/stocktakingcollections', data=data, role='admin') # Insert the third stocktaking. (Product 1 is not included) stocktakings = [{ 'product_id': 2, 'count': 50 }, { 'product_id': 3, 'count': 25 }, { 'product_id': 4, 'count': 33 }] t = datetime.strptime('2018-04-02 09:00:00', '%Y-%m-%d %H:%M:%S') data = {'stocktakings': stocktakings, 'timestamp': int(t.timestamp())} self.post(url='/stocktakingcollections', data=data, role='admin') # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=3).first() self.assertTrue(1 in [s.product_id for s in start.stocktakings]) self.assertFalse(1 in [s.product_id for s in end.stocktakings]) result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 90) self.assertEqual(products[2]['purchase_count'], 0) self.assertEqual(products[3]['purchase_count'], 0) self.assertEqual(products[4]['purchase_count'], 0) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 27000) self.assertEqual(products[2]['purchase_sum_price'], 0) self.assertEqual(products[3]['purchase_sum_price'], 0) self.assertEqual(products[4]['purchase_sum_price'], 0) # Check replenish count self.assertEqual(products[1]['replenish_count'], 0) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 0) self.assertEqual(products[4]['replenish_count'], 0) # Check differences self.assertEqual(products[1]['difference'], -10) self.assertEqual(products[2]['difference'], 0) self.assertEqual(products[3]['difference'], 0) self.assertEqual(products[4]['difference'], 0) # Check balance self.assertEqual(products[1]['balance'], -10 * 300) self.assertEqual(products[2]['balance'], 0) self.assertEqual(products[3]['balance'], 0) self.assertEqual(products[4]['balance'], 0) # Check overall balance self.assertEqual(result['balance'], -3000) self.assertEqual(result['loss'], 3000) self.assertEqual(result['profit'], 0)
def test_balance_between_stocktakings_forgotten_purchases_inactive_products( self): """ TODO """ # Since the start of this test is the same as the previous one, it # can be run again to generate the required data. self.test_balance_between_stocktakings_multiple_stocktakings() # Ooops. We forgot to insert some two purchases (inactive products). Lets do it now (AFTER THIRD STOCKTAKING!) self.assertFalse(Product.query.filter_by(id=4).first().active) t = datetime.strptime('2018-04-02 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=3, product_id=4, amount=2, timestamp=t)) # Insert the fourth stocktaking. t = datetime.strptime('2018-04-05 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(StocktakingCollection(admin_id=1, timestamp=t)) db.session.flush() stocktakings = [{ 'product_id': 1, 'count': 40 }, { 'product_id': 2, 'count': 5 }, { 'product_id': 3, 'count': 5 }, { 'product_id': 4, 'count': 0 }] for s in stocktakings: db.session.add(Stocktaking(collection_id=4, **s)) db.session.commit() # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=4).first() result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 9) self.assertEqual(products[2]['purchase_count'], 15) self.assertEqual(products[3]['purchase_count'], 13) self.assertEqual(products[4]['purchase_count'], 2) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 2700) self.assertEqual(products[2]['purchase_sum_price'], 750) self.assertEqual(products[3]['purchase_sum_price'], 1300) self.assertEqual(products[4]['purchase_sum_price'], 400) # Check replenish count self.assertEqual(products[1]['replenish_count'], 10) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 5) self.assertEqual(products[4]['replenish_count'], 0) # Check differences self.assertEqual(products[1]['difference'], -61) self.assertEqual(products[2]['difference'], -30) self.assertEqual(products[3]['difference'], -12) self.assertEqual(products[4]['difference'], -31) # Check balance self.assertEqual(products[1]['balance'], -61 * 300) self.assertEqual(products[2]['balance'], -30 * 50) self.assertEqual(products[3]['balance'], -12 * 100) self.assertEqual(products[4]['balance'], -31 * 200) # Check overall balance self.assertEqual(result['balance'], -27200) self.assertEqual(result['loss'], 27200) self.assertEqual(result['profit'], 0)
def test_balance_between_stocktakings_multiple_stocktakings(self): """ This test checks whether the calculation of the balance works correctly over several stocktakings. """ # Since the start of this test is the same as the previous one, it # can be run again to generate the required data. self.test_balance_between_stocktakings_two_stocktakings() # Insert some purchases between the second and the third stocktaking t = datetime.strptime('2018-03-05 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=1, product_id=1, amount=2, timestamp=t)) t = datetime.strptime('2018-03-06 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add( Purchase(user_id=1, product_id=2, amount=10, timestamp=t)) t = datetime.strptime('2018-03-07 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=2, product_id=3, amount=5, timestamp=t)) t = datetime.strptime('2018-03-08 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(Purchase(user_id=3, product_id=1, amount=4, timestamp=t)) # Insert the third stocktaking. t = datetime.strptime('2018-04-01 09:00:00', '%Y-%m-%d %H:%M:%S') db.session.add(StocktakingCollection(admin_id=1, timestamp=t)) db.session.flush() stocktakings = [{ 'product_id': 1, 'count': 40 }, { 'product_id': 2, 'count': 5 }, { 'product_id': 3, 'count': 5 }, { 'product_id': 4, 'count': 0 }] for s in stocktakings: db.session.add(Stocktaking(collection_id=3, **s)) # The count of product 4 is 0 so the product will be set inactive! Product.query.filter_by(id=4).first().active = False db.session.commit() # Query the stocktakings. start = StocktakingCollection.query.filter_by(id=1).first() end = StocktakingCollection.query.filter_by(id=3).first() # Timeline # Date Event # # 01.01.2017 Purchase: 100 x Product 1 # 01.01.2017 Replenishment 1 # (Both are not taken into account for the calculation) # ----------------------------------- # 01.01.2018 Stocktaking 1 # - Product 1: 100 # - Product 2: 50 # - Product 3: 25 # - Product 4: 33 # # 01.02.2018 Purchase: 1 x Product 1 # 02.02.2018 Purchase: 5 x Product 2 # 03.02.2018 Purchase: 8 x Product 3 # 04.02.2018 Purchase: 2 x Product 1 # ----------------------------------- # 05.02.2018 Replenishment 2 # - Product 1: 10 # - Product 3: 5 # # ----------------------------------- # 05.03.2018 Purchase: 2 x Product 1 # 06.03.2018 Purchase: 10 x Product 2 # 07.03.2018 Purchase: 5 x Product 3 # 08.03.2018 Purchase: 4 x Product 1 # ----------------------------------- # 01.04.2018 Stocktaking 3 # - Product 1: 40 # - Product 2: 5 # - Product 3: 5 # - Product 4: 0 # ----------------------------------- # Balance: # Product 1: 100 (start) - 9 (purchase) + 10 (replenishment) = 101 # Product 2: 50 (start) - 15 (purchase) + 0 (replenishment) = 35 # Product 3: 25 (start) - 13 (purchase) + 5 (replenishment) = 17 # Product 4: 33 (start) - 0 (purchase) + 0 (replenishment) = 33 # ----------------------------------- # Loss between first and third stocktaking # Product 1: 101 - 40 = 61 # Product 2: 35 - 5 = 30 # Product 3: 17 - 5 = 12 # Product 4: 33 - 0 = 33 result = stocktaking_helpers._get_balance_between_stocktakings( start, end) self.assertTrue('products' in result) products = result['products'] # Check if all products are in the balance self.assertEqual({1, 2, 3, 4}, set(products.keys())) # Check purchase count self.assertEqual(products[1]['purchase_count'], 9) self.assertEqual(products[2]['purchase_count'], 15) self.assertEqual(products[3]['purchase_count'], 13) self.assertEqual(products[4]['purchase_count'], 0) # Check purchase sum price self.assertEqual(products[1]['purchase_sum_price'], 2700) self.assertEqual(products[2]['purchase_sum_price'], 750) self.assertEqual(products[3]['purchase_sum_price'], 1300) self.assertEqual(products[4]['purchase_sum_price'], 0) # Check replenish count self.assertEqual(products[1]['replenish_count'], 10) self.assertEqual(products[2]['replenish_count'], 0) self.assertEqual(products[3]['replenish_count'], 5) self.assertEqual(products[4]['replenish_count'], 0) # Check differences self.assertEqual(products[1]['difference'], -61) self.assertEqual(products[2]['difference'], -30) self.assertEqual(products[3]['difference'], -12) self.assertEqual(products[4]['difference'], -33) # Check balance self.assertEqual(products[1]['balance'], -61 * 300) self.assertEqual(products[2]['balance'], -30 * 50) self.assertEqual(products[3]['balance'], -12 * 100) self.assertEqual(products[4]['balance'], -33 * 200) # Check overall balance self.assertEqual(result['balance'], -27600) self.assertEqual(result['loss'], 27600) self.assertEqual(result['profit'], 0)
def get_financial_overview(admin): """ The financial status of the entire project can be retrieved via this route. All purchases, deposits, payoffs, refunds and replenishmentcollections are used for this purpose. The items are cleared once to a number indicating whether the community has debt or surplus money. In addition, the individual items are returned separately in order to get a better breakdown of the items. :param admin: Is the administrator user, determined by @adminRequired. :return: A dictionary with the individually calculated values. """ # Query all purchases, purchases = Purchase.query.filter(Purchase.revoked.is_(False)).all() # Query all deposits. deposits = Deposit.query.filter(Deposit.revoked.is_(False)).all() # Query all turnovers. turnovers = Turnover.query.filter(Turnover.revoked.is_(False)).all() # Query all payoffs. payoffs = Payoff.query.filter(Payoff.revoked.is_(False)).all() # Query all refunds. refunds = Refund.query.filter(Refund.revoked.is_(False)).all() # Query all replenishment collections. replcolls = (ReplenishmentCollection.query.filter( ReplenishmentCollection.revoked.is_(False)).all()) # Get the balance between the first and the last stocktaking. # If there is no stocktaking or only one stocktaking, the balance is 0. stock_first = (StocktakingCollection.query.order_by( StocktakingCollection.id).first()) stock_last = (StocktakingCollection.query.order_by( StocktakingCollection.id.desc()).first()) if not all([stock_first, stock_last]) or stock_first is stock_last: pos_stock = 0 neg_stock = 0 else: balance = _get_balance_between_stocktakings(stock_first, stock_last) pos_stock = balance['profit'] neg_stock = balance['loss'] # Incomes are: # - Purchases with a positive price # - Deposits with a positive amount # - Turnovers with a positive amount # - Replenishmentcollections with a negative price # - Refunds with a negative amount # - Payoffs with a negative amount # - Profits between stocktakings pos_pur = sum( map( abs, list( filter(lambda x: x >= 0, list(map(lambda x: x.price, purchases)))))) pos_dep = sum( map( abs, list( filter(lambda x: x >= 0, list(map(lambda x: x.amount, deposits)))))) pos_turn = sum( map( abs, list( filter(lambda x: x >= 0, list(map(lambda x: x.amount, turnovers)))))) neg_rep = sum( map( abs, list( filter(lambda x: x < 0, list(map(lambda x: x.price, replcolls)))))) neg_ref = sum( map( abs, list( filter(lambda x: x < 0, list(map(lambda x: x.total_price, refunds)))))) neg_pay = sum( map( abs, list( filter(lambda x: x < 0, list(map(lambda x: x.amount, payoffs)))))) sum_incomes = sum( [pos_pur, pos_dep, pos_turn, neg_rep, neg_ref, neg_pay, pos_stock]) incomes = { 'amount': sum_incomes, 'items': [{ 'name': 'Purchases', 'amount': pos_pur }, { 'name': 'Deposits', 'amount': pos_dep }, { 'name': 'Turnovers', 'amount': pos_turn }, { 'name': 'Replenishments', 'amount': neg_rep }, { 'name': 'Refunds', 'amount': neg_ref }, { 'name': 'Payoffs', 'amount': neg_pay }, { 'name': 'Stocktakings', 'amount': pos_stock }] } # Expenses are: # - Purchases with a negative price # - Deposits with a negative amount # - Turnovers with a negative amount # - Replenishmentcollections with a positive price # - Refunds with a positive amount # - Payoffs with a positive amount # - Losses between stocktakings neg_pur = sum( map( abs, list( filter(lambda x: x < 0, list(map(lambda x: x.price, purchases)))))) neg_dep = sum( map( abs, list( filter(lambda x: x < 0, list(map(lambda x: x.amount, deposits)))))) neg_turn = sum( map( abs, list( filter(lambda x: x < 0, list(map(lambda x: x.amount, turnovers)))))) pos_rep = sum( map( abs, list( filter(lambda x: x >= 0, list(map(lambda x: x.price, replcolls)))))) pos_ref = sum( map( abs, list( filter(lambda x: x >= 0, list(map(lambda x: x.total_price, refunds)))))) pos_pay = sum( map( abs, list( filter(lambda x: x >= 0, list(map(lambda x: x.amount, payoffs)))))) sum_expenses = sum( [neg_pur, neg_dep, neg_turn, pos_rep, pos_ref, pos_pay, neg_stock]) expenses = { 'amount': sum_expenses, 'items': [{ 'name': 'Purchases', 'amount': neg_pur }, { 'name': 'Deposits', 'amount': neg_dep }, { 'name': 'Turnovers', 'amount': neg_turn }, { 'name': 'Replenishments', 'amount': pos_rep }, { 'name': 'Refunds', 'amount': pos_ref }, { 'name': 'Payoffs', 'amount': pos_pay }, { 'name': 'Stocktakings', 'amount': neg_stock }] } # The total balance is calculated as incomes minus expenses. total_balance = sum_incomes - sum_expenses financial_overview = { 'total_balance': total_balance, 'incomes': incomes, 'expenses': expenses } return jsonify(financial_overview), 200