test: Stock Ageing FIFO Slot generation
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index 88e0712..e6dfc97 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -292,7 +292,7 @@
 					fifo_queue[0][0] += flt(row.actual_qty)
 					fifo_queue[0][1] = row.posting_date
 				else:
-					fifo_queue.append([row.actual_qty, row.posting_date])
+					fifo_queue.append([flt(row.actual_qty), row.posting_date])
 				return
 
 			for serial_no in serial_nos:
@@ -395,10 +395,10 @@
 
 		warehouse_results = (
 			frappe.qb.from_(warehouse)
-				.select("name").where(
-					(warehouse.lft >= lft)
-					& (warehouse.rgt <= rgt)
-				).run()
+			.select("name").where(
+				(warehouse.lft >= lft)
+				& (warehouse.rgt <= rgt)
+			).run()
 		)
 		warehouse_results = [x[0] for x in warehouse_results]
 
diff --git a/erpnext/stock/report/stock_ageing/test_stock_ageing.py b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
new file mode 100644
index 0000000..0e355a5
--- /dev/null
+++ b/erpnext/stock/report/stock_ageing/test_stock_ageing.py
@@ -0,0 +1,125 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import frappe
+
+from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots
+from erpnext.tests.utils import ERPNextTestCase
+
+class TestStockAgeing(ERPNextTestCase):
+	def setUp(self) -> None:
+		self.filters = frappe._dict(
+			company="_Test Company",
+			to_date="2021-12-10"
+		)
+
+	def test_normal_inward_outward_queue(self):
+		"Reference: Case 1 in stock_ageing_fifo_logic.md"
+		sle = [
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=30, qty_after_transaction=30,
+				posting_date="2021-12-01", voucher_type="Stock Entry",
+				voucher_no="001",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=20, qty_after_transaction=50,
+				posting_date="2021-12-02", voucher_type="Stock Entry",
+				voucher_no="002",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=(-10), qty_after_transaction=40,
+				posting_date="2021-12-03", voucher_type="Stock Entry",
+				voucher_no="003",
+				has_serial_no=False, serial_no=None
+			)
+		]
+
+		slots = FIFOSlots(self.filters, sle).generate()
+
+		self.assertTrue(slots["Flask Item"]["fifo_queue"])
+		result = slots["Flask Item"]
+		queue = result["fifo_queue"]
+
+		self.assertEqual(result["qty_after_transaction"], result["total_qty"])
+		self.assertEqual(queue[0][0], 20.0)
+
+	def test_insufficient_balance(self):
+		"Reference: Case 3 in stock_ageing_fifo_logic.md"
+		sle = [
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=(-30), qty_after_transaction=(-30),
+				posting_date="2021-12-01", voucher_type="Stock Entry",
+				voucher_no="001",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=20, qty_after_transaction=(-10),
+				posting_date="2021-12-02", voucher_type="Stock Entry",
+				voucher_no="002",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=20, qty_after_transaction=10,
+				posting_date="2021-12-03", voucher_type="Stock Entry",
+				voucher_no="003",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=10, qty_after_transaction=20,
+				posting_date="2021-12-03", voucher_type="Stock Entry",
+				voucher_no="004",
+				has_serial_no=False, serial_no=None
+			)
+		]
+
+		slots = FIFOSlots(self.filters, sle).generate()
+
+		result = slots["Flask Item"]
+		queue = result["fifo_queue"]
+
+		self.assertEqual(result["qty_after_transaction"], result["total_qty"])
+		self.assertEqual(queue[0][0], 10.0)
+		self.assertEqual(queue[1][0], 10.0)
+
+	def test_stock_reconciliation(self):
+		sle = [
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=30, qty_after_transaction=30,
+				posting_date="2021-12-01", voucher_type="Stock Entry",
+				voucher_no="001",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=0, qty_after_transaction=50,
+				posting_date="2021-12-02", voucher_type="Stock Reconciliation",
+				voucher_no="002",
+				has_serial_no=False, serial_no=None
+			),
+			frappe._dict(
+				name="Flask Item",
+				actual_qty=(-10), qty_after_transaction=40,
+				posting_date="2021-12-03", voucher_type="Stock Entry",
+				voucher_no="003",
+				has_serial_no=False, serial_no=None
+			)
+		]
+
+		slots = FIFOSlots(self.filters, sle).generate()
+
+		result = slots["Flask Item"]
+		queue = result["fifo_queue"]
+
+		self.assertEqual(result["qty_after_transaction"], result["total_qty"])
+		self.assertEqual(queue[0][0], 20.0)
+		self.assertEqual(queue[1][0], 20.0)