refactor: use DB to store tree and state
diff --git a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json
index c5715fe..0de820a 100644
--- a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json
+++ b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json
@@ -13,8 +13,7 @@
   "column_break_iwny",
   "algorithm",
   "section_break_zbty",
-  "current_node",
-  "tree"
+  "current_node"
  ],
  "fields": [
   {
@@ -23,12 +22,12 @@
   },
   {
    "fieldname": "from_date",
-   "fieldtype": "Date",
+   "fieldtype": "Datetime",
    "label": "From Date"
   },
   {
    "fieldname": "to_date",
-   "fieldtype": "Date",
+   "fieldtype": "Datetime",
    "label": "To Date"
   },
   {
@@ -47,21 +46,17 @@
    "fieldtype": "Section Break"
   },
   {
-   "fieldname": "tree",
-   "fieldtype": "JSON",
-   "label": "Tree"
-  },
-  {
    "fieldname": "current_node",
-   "fieldtype": "JSON",
-   "label": "Current Node"
+   "fieldtype": "Link",
+   "label": "Current Node",
+   "options": "Nodes"
   }
  ],
  "hide_toolbar": 1,
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-09-25 21:15:47.905386",
+ "modified": "2023-09-26 12:09:23.649156",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bisect Accounting Statements",
diff --git a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py
index 53319b3..8730772e 100644
--- a/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py
+++ b/erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.py
@@ -2,7 +2,6 @@
 # For license information, please see license.txt
 
 import datetime
-import json
 from collections import deque
 from math import floor
 
@@ -11,184 +10,10 @@
 from frappe import _
 from frappe.model.document import Document
 from frappe.utils import getdate
-from frappe.utils.data import DATETIME_FORMAT, guess_date_format
-
-
-class Node(object):
-	def __init__(
-		self,
-		parent: int = None,
-		period: (datetime, datetime) = None,
-		left: int = None,
-		right: int = None,
-	):
-		self.parent = parent
-		self.left_child = left
-		self.right_child = right
-
-		self.period = period
-		self.difference = 0.0
-		self.profit_and_loss_summary = 0.0
-		self.balance_sheet_summary = 0.0
-
-	def as_dict(self):
-		return dict(
-			parent=self.parent,
-			left_child=self.left_child,
-			right_child=self.right_child,
-			period=(self.period[0].strftime(DATETIME_FORMAT), self.period[1].strftime(DATETIME_FORMAT)),
-			difference=self.difference,
-			profit_and_loss_summary=self.profit_and_loss_summary,
-			balance_sheet_summary=self.balance_sheet_summary,
-		)
-
-	def __repr__(self):
-		return f"Node (parent: {self.parent}, left_child: {self.left_child}, right_child: {self.right_child}, period: {self.period})"
-
-
-class BTree(object):
-	def __init__(self):
-		self.btree = []
-		self.current_node = None
-
-	def as_list(self):
-		lst = []
-		for x in self.btree:
-			lst.append(x.as_dict())
-		return lst
-
-	def bfs(self, from_date: datetime, to_date: datetime):
-		node = frappe.new_doc("Nodes")
-		node.period_from_date = from_date
-		node.period_to_date = to_date
-		node.root = None
-		node.insert()
-
-		period_list = deque([node])
-
-		while period_list:
-			cur_node = period_list.popleft()
-
-			print(cur_node.as_dict())
-			delta = cur_node.period_to_date - cur_node.period_from_date
-			if delta.days == 0:
-				continue
-			else:
-				cur_floor = floor(delta.days / 2)
-				left = (
-					cur_node.period_from_date,
-					(cur_node.period_from_date + relativedelta(days=+cur_floor)),
-				)
-				left_node = frappe.get_doc(
-					{
-						"doctype": "Nodes",
-						"period_from_date": cur_node.period_from_date,
-						"period_to_date": left,
-						"root": cur_node.name,
-					}
-				).insert()
-				cur_node.left_child = left_node.name
-				period_list.append(left_node)
-
-				right = (
-					(cur_node.period_from_date + relativedelta(days=+(cur_floor + 1))),
-					cur_node.period_to_date,
-				)
-				right_node = frappe.get_doc(
-					{
-						"doctype": "Nodes",
-						"period_from_date": right,
-						"period_to_date": cur_node.period_to_date,
-						"root": cur_node.name,
-					}
-				).insert()
-				cur_node.right_child = right_node
-				period_list.append(right_node)
-
-	def dfs(self, from_date: datetime, to_date: datetime):
-		root_node = Node(parent=None, period=(getdate(from_date), getdate(to_date)))
-		root_node.parent = None
-
-		# add root node to tree
-		self.btree.append(root_node)
-		cur_node = root_node
-		period_list = [root_node]
-
-		while period_list:
-			cur_node = period_list.pop()
-			cur_node_index = len(self.btree) - 1
-
-			delta = cur_node.period[1] - cur_node.period[0]
-			if delta.days == 0:
-				continue
-			else:
-				cur_floor = floor(delta.days / 2)
-				left = (cur_node.period[0], (cur_node.period[0] + relativedelta(days=+cur_floor)))
-				left_node = Node(parent=cur_node_index, period=left)
-				self.btree.append(left_node)
-				cur_node.left_child = len(self.btree) - 1
-				period_list.append(left_node)
-
-				right = ((cur_node.period[0] + relativedelta(days=+(cur_floor + 1))), cur_node.period[1])
-				right_node = Node(parent=cur_node_index, period=right)
-				self.btree.append(right_node)
-				cur_node.right_child = len(self.btree) - 1
-				period_list.append(right_node)
-
-	def load_tree(self, tree: list, current_node: dict):
-		self.btree = []
-		tree = json.loads(tree)
-		for x in tree:
-			x = frappe._dict(x)
-			n = Node(x.parent, None, x.left_child, x.right_child)
-			date_format = guess_date_format(x.period[0])
-			n.period = (
-				datetime.datetime.strptime(x.period[0], date_format),
-				datetime.datetime.strptime(x.period[1], date_format),
-			)
-			n.difference = x.difference
-			x.profit_and_loss_summary = x.profit_and_loss_summary
-			x.balance_sheet_summary = x.balance_sheet_summary
-			self.btree.append(n)
-
-		current_node = frappe._dict(json.loads(current_node))
-		n = Node(
-			current_node.parent, current_node.period, current_node.left_child, current_node.right_child
-		)
-		n.period = current_node.period
-		n.difference = current_node.difference
-		n.profit_and_loss_summary = current_node.profit_and_loss_summary
-		n.balance_sheet_summary = current_node.balance_sheet_summary
-		self.current_node = n
-
-	def build_tree(self, from_date: datetime, to_date: datetime, alogrithm: str):
-		frappe.db.delete("Nodes")
-		if alogrithm == "BFS":
-			self.bfs(from_date, to_date)
-
-		if alogrithm == "DFS":
-			self.dfs(from_date, to_date)
-
-		# set root as current node
-		self.current_node = self.btree[0]
-
-	def bisec_left(self):
-		pass
-
-	def bisect_right(self):
-		pass
-
-	def move_up(self):
-		pass
+from frappe.utils.data import guess_date_format
 
 
 class BisectAccountingStatements(Document):
-	def __init__(self, *args, **kwargs):
-		super(BisectAccountingStatements, self).__init__(*args, **kwargs)
-		if self.tree and self.current_node:
-			self.tree_instance = BTree()
-			self.tree_instance.load_tree(self.tree, self.current_node)
-
 	def validate(self):
 		self.validate_dates()
 
@@ -200,45 +25,132 @@
 				)
 			)
 
+	def bfs(self, from_date: datetime, to_date: datetime):
+		# Make Root node
+		node = frappe.new_doc("Nodes")
+		node.root = None
+		node.period_from_date = from_date
+		node.period_to_date = to_date
+		node.insert()
+
+		period_queue = deque([node])
+		while period_queue:
+			cur_node = period_queue.popleft()
+			delta = cur_node.period_to_date - cur_node.period_from_date
+			if delta.days == 0:
+				continue
+			else:
+				cur_floor = floor(delta.days / 2)
+				next_to_date = cur_node.period_from_date + relativedelta(days=+cur_floor)
+				left_node = frappe.new_doc("Nodes")
+				left_node.period_from_date = cur_node.period_from_date
+				left_node.period_to_date = next_to_date
+				left_node.root = cur_node.name
+				left_node.insert()
+				cur_node.left_child = left_node.name
+				period_queue.append(left_node)
+
+				next_from_date = cur_node.period_from_date + relativedelta(days=+(cur_floor + 1))
+				right_node = frappe.new_doc("Nodes")
+				right_node.period_from_date = next_from_date
+				right_node.period_to_date = cur_node.period_to_date
+				right_node.root = cur_node.name
+				right_node.insert()
+				cur_node.right_child = right_node.name
+				period_queue.append(right_node)
+
+				cur_node.save()
+
+	def dfs(self, from_date: datetime, to_date: datetime):
+		# Make Root node
+		node = frappe.new_doc("Nodes")
+		node.root = None
+		node.period_from_date = from_date
+		node.period_to_date = to_date
+		node.insert()
+
+		period_stack = [node]
+		while period_stack:
+			cur_node = period_stack.pop()
+			delta = cur_node.period_to_date - cur_node.period_from_date
+			if delta.days == 0:
+				continue
+			else:
+				cur_floor = floor(delta.days / 2)
+				next_to_date = cur_node.period_from_date + relativedelta(days=+cur_floor)
+				left_node = frappe.new_doc("Nodes")
+				left_node.period_from_date = cur_node.period_from_date
+				left_node.period_to_date = next_to_date
+				left_node.root = cur_node.name
+				left_node.insert()
+				cur_node.left_child = left_node.name
+				period_stack.append(left_node)
+
+				next_from_date = cur_node.period_from_date + relativedelta(days=+(cur_floor + 1))
+				right_node = frappe.new_doc("Nodes")
+				right_node.period_from_date = next_from_date
+				right_node.period_to_date = cur_node.period_to_date
+				right_node.root = cur_node.name
+				right_node.insert()
+				cur_node.right_child = right_node.name
+				period_stack.append(right_node)
+
+				cur_node.save()
+
 	@frappe.whitelist()
 	def build_tree(self):
-		self.tree_instance = BTree()
-		self.tree_instance.build_tree(self.from_date, self.to_date, self.algorithm)
-		print("printing tree")
-		for x in self.tree_instance.btree:
-			print(x)
+		frappe.db.delete("Nodes")
 
-		print("Root", self.tree_instance.current_node)
+		# Convert str to datetime format
+		dt_format = guess_date_format(self.from_date)
+		from_date = datetime.datetime.strptime(self.from_date, dt_format)
+		to_date = datetime.datetime.strptime(self.to_date, dt_format)
 
-		self.tree = json.dumps(self.tree_instance.as_list())
-		self.current_node = json.dumps(self.tree_instance.btree[0].as_dict())
+		if self.algorithm == "BFS":
+			self.bfs(from_date, to_date)
+
+		if self.algorithm == "DFS":
+			self.dfs(from_date, to_date)
+
+		# set root as current node
+		root = frappe.db.get_all("Nodes", filters={"root": ["is", "not set"]})[0]
+		frappe.db.set_single_value("Bisect Accounting Statements", "current_node", root.name)
 
 	@frappe.whitelist()
 	def bisect_left(self):
-		if self.tree_instance.current_node is not None:
-			if self.tree_instance.current_node.left_child is not None:
-				self.current_node = self.tree_instance.btree[self.tree_instance.current_node.left_child]
-				self.current_node = json.dumps(self.current_node.as_dict())
+		if self.current_node is not None:
+			cur_node = frappe.get_doc("Nodes", self.current_node)
+			if cur_node.left_child is not None:
+				lft_node = frappe.get_doc("Nodes", cur_node.left_child)
+				self.current_node = cur_node.left_child
+				self.from_date = lft_node.period_from_date
+				self.to_date = lft_node.period_to_date
 				self.save()
 			else:
 				frappe.msgprint("No more children on Left")
 
 	@frappe.whitelist()
 	def bisect_right(self):
-		if self.tree_instance.current_node is not None:
-			if self.tree_instance.current_node.right_child is not None:
-				self.current_node = self.tree_instance.btree[self.tree_instance.current_node.right_child]
-				self.current_node = json.dumps(self.current_node.as_dict())
+		if self.current_node is not None:
+			cur_node = frappe.get_doc("Nodes", self.current_node)
+			if cur_node.right_child is not None:
+				rgt_node = frappe.get_doc("Nodes", cur_node.right_child)
+				self.current_node = cur_node.right_child
+				self.from_date = rgt_node.period_from_date
+				self.to_date = rgt_node.period_to_date
 				self.save()
 			else:
 				frappe.msgprint("No more children on Right")
 
 	@frappe.whitelist()
 	def move_up(self):
-		if self.tree_instance.current_node is not None:
-			if self.tree_instance.current_node.parent is not None:
-				self.current_node = self.tree_instance.btree[self.tree_instance.current_node.parent]
-				self.current_node = json.dumps(self.current_node.as_dict())
+		if self.current_node is not None:
+			cur_node = frappe.get_doc("Nodes", self.current_node)
+			if cur_node.root is not None:
+				root = frappe.get_doc("Nodes", cur_node.root)
+				self.current_node = cur_node.root
+				self.from_date = root.period_from_date
+				self.to_date = root.period_to_date
 				self.save()
 			else:
 				frappe.msgprint("Reached Root")