Skip to content

Commit

Permalink
Merge pull request #262 from RitvikSardana/develop-ritvik-multi-accou…
Browse files Browse the repository at this point in the history
…nt-category

feat: multiple account and cost center support for fee category
  • Loading branch information
RitvikSardana authored Jun 29, 2024
2 parents 3af4bb0 + 6c037e6 commit 5e80531
Show file tree
Hide file tree
Showing 15 changed files with 983 additions and 633 deletions.
1 change: 0 additions & 1 deletion education/education/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,6 @@ def get_school_abbr_logo():

@frappe.whitelist()
def get_student_attendance(student, student_group):
print(student, student_group, "student,student_group")
return frappe.db.get_list(
"Student Attendance",
filters={"student": student, "student_group": student_group, "docstatus": 1},
Expand Down
11 changes: 9 additions & 2 deletions education/education/doctype/fee_category/fee_category.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"field_order": [
"category_name",
"description",
"item"
"item",
"item_defaults"
],
"fields": [
{
Expand Down Expand Up @@ -39,11 +40,17 @@
"label": "Item",
"options": "Item",
"read_only": 1
},
{
"fieldname": "item_defaults",
"fieldtype": "Table",
"label": "Accounting Defaults",
"options": "Fee Category Default"
}
],
"icon": "fa fa-flag",
"links": [],
"modified": "2024-01-07 14:03:30.856221",
"modified": "2024-05-28 12:32:35.849279",
"modified_by": "Administrator",
"module": "Education",
"name": "Fee Category",
Expand Down
99 changes: 94 additions & 5 deletions education/education/doctype/fee_category/fee_category.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@

import frappe
from frappe.model.document import Document
from frappe import _


class FeeCategory(Document):
def validate(self):
self.update_defaults_from_item_group()
self.validate_duplicate_item_defaults()

def after_insert(self):
# create an item
item_name = create_item(self)
Expand All @@ -21,6 +26,32 @@ def on_trash(self):
# delete item
frappe.delete_doc("Item", self.name, force=1)

def update_defaults_from_item_group(self):
"""Get defaults from Item Group"""
item_group = "Fee Component"
if self.item_defaults:
return

item_defaults = frappe.db.get_values(
"Item Default",
{"parent": item_group},
[
"company",
"selling_cost_center",
"income_account",
],
as_dict=1,
)
if item_defaults:
update_item_defaults(self, item_defaults)
frappe.msgprint(_('Defaults fetched from "Fee Component" Item Group '), alert=True)

def validate_duplicate_item_defaults(self):
"""Validate duplicate item defaults"""
companies = [d.company for d in self.item_defaults]
if len(companies) != len(set(companies)):
frappe.throw(_("Cannot set multiple Item Defaults for a company."))


def create_item(doc, use_name_field=True):
name_field = doc.name if use_name_field else doc.fees_category
Expand All @@ -34,14 +65,72 @@ def create_item(doc, use_name_field=True):
item.is_sales_item = 1
item.is_service_item = 1
item.is_stock_item = 0

update_item_defaults(item, doc.item_defaults)
item.insert()
return item.name


def update_item(doc):
item = frappe.get_doc("Item", doc.name)
item.item_name = doc.name
item.description = doc.description
def update_item(fee_category):
item = frappe.get_doc("Item", fee_category.name)
item.item_name = fee_category.name
item.description = fee_category.description

item_defaults = frappe.get_all(
"Item Default",
{"parent": fee_category.item},
["company", "selling_cost_center", "income_account"],
)
item_default_companies = [d.company for d in item_defaults]
fee_category_companies = [d.company for d in fee_category.item_defaults]
for fee_category_default in fee_category.item_defaults:
if fee_category_default.company not in item_default_companies:
add_item_defaults(item, fee_category_default)
else:
update_defaults(item, fee_category_default)

remove_item_defaults(item, fee_category_companies)
item.save()
return item.name


def add_item_defaults(item, fee_category_defaults):
item.append(
"item_defaults",
{
"company": fee_category_defaults.company,
"selling_cost_center": fee_category_defaults.selling_cost_center,
"income_account": fee_category_defaults.income_account,
"default_warehouse": "",
},
)


def update_defaults(item, fee_category_default):
for d in item.item_defaults:
if d.company == fee_category_default.company:
d.selling_cost_center = fee_category_default.selling_cost_center
d.income_account = fee_category_default.income_account
break


def remove_item_defaults(item, fee_category_companies):
items_to_remove = []
for d in item.item_defaults:
if d.company not in fee_category_companies:
items_to_remove.append(d.idx)

for idx in items_to_remove:
item.item_defaults.pop(idx - 1)


def update_item_defaults(item, defaults):
for item_default in defaults:
item.append(
"item_defaults",
{
"company": item_default.company,
"selling_cost_center": item_default.selling_cost_center,
"income_account": item_default.income_account,
"default_warehouse": "",
},
)
107 changes: 106 additions & 1 deletion education/education/doctype/fee_category/test_fee_category.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,113 @@
# Copyright (c) 2015, Frappe Technologies and Contributors
# See license.txt

import frappe
from frappe.tests.utils import FrappeTestCase
from education.education.test_utils import (
create_fee_category,
create_company,
get_defaults,
)


class TestFeeCategory(FrappeTestCase):
pass
def setUp(self):
create_fee_category("Tuition Fee")
create_company("Dunder Mifflin Paper Co")

def tearDown(self):
frappe.db.rollback()

def test_item_created(self):
"""
Test to check if the item master is created when a Fee Category is created.
"""
companies = frappe.db.get_all("Company", fields=["name"])
item = frappe.db.get_value(
"Fee Category", filters={"name": "Tuition Fee"}, fieldname="item"
)
self.assertTrue(frappe.db.exists("Item", item))

item_group = frappe.db.get_value(
"Item", filters={"name": item}, fieldname="item_group"
)
self.assertEqual(item_group, "Fee Component")

def test_item_defaults_from_item_group(self):
"""
When creating a Fee Category, if there are no item defaults, then get defaults from Item Group
"""
defaults = get_defaults()

item_group = frappe.get_doc("Item Group", "Fee Component")
item_group.append(
"item_group_defaults",
{
"company": "_Test Company",
"income_account": defaults.default_income_account,
"selling_cost_center": defaults.cost_center,
},
)
item_group.save()

fee_category = frappe.get_doc("Fee Category", "Tuition Fee")
fee_category.description = "Test"
fee_category.save()

fee_category = frappe.get_doc("Fee Category", "Tuition Fee")
fee_category_defaults = fee_category.get("item_defaults")[0]

self.assertEqual(fee_category_defaults.company, "_Test Company")
self.assertEqual(
fee_category_defaults.income_account, defaults.default_income_account
)
self.assertEqual(fee_category_defaults.selling_cost_center, defaults.cost_center)

def test_fee_component_defaults_same_as_item_defaults(self):
"""
When creating a Fee Category, if the defaults are set in Fee Category those should be saved in the Item Defaults aswell
"""
defaults = get_defaults()

fee_category_name = "Test Fee Category"
fee_category = create_fee_category(fee_category_name)
fee_category.append(
"item_defaults",
{
"company": "_Test Company",
"income_account": defaults.default_income_account,
"selling_cost_center": defaults.cost_center,
},
)
fee_category.save()

item = frappe.get_doc("Item", fee_category_name)
item_defaults = item.get("item_defaults")[0]

self.assertEqual(item_defaults.company, "_Test Company")
self.assertEqual(item_defaults.income_account, defaults.default_income_account)
self.assertEqual(item_defaults.selling_cost_center, defaults.cost_center)

def test_fee_component_duplicate_default(self):
"""
When setting defaults if there are 2 defaults for the same company, then throw an error
"""
fee_component = frappe.get_doc("Fee Category", "Tuition Fee")
# get any income account
income_account = frappe.get_all("Account", fields=["name"], limit=1)[0]["name"]
defaults = get_defaults()
default_array = [
{
"company": "_Test Company",
"income_account": income_account,
"selling_cost_center": defaults.cost_center,
},
{
"company": "_Test Company",
"income_account": income_account,
"selling_cost_center": defaults.cost_center,
},
]
for default in default_array:
fee_component.append("item_defaults", default)
self.assertRaises(frappe.ValidationError, fee_component.save)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2024-05-28 11:36:10.928607",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"income_account",
"selling_cost_center"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1
},
{
"fieldname": "selling_cost_center",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Default Cost Center",
"link_filters": "[[\"Cost Center\",\"is_group\",\"=\",0],[\"Cost Center\",\"company\",\"=\",\"eval: doc.company\"]]",
"options": "Cost Center"
},
{
"fieldname": "income_account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Default Income Account",
"link_filters": "[[\"Account\",\"company\",\"=\",\"eval: doc.company\"],[\"Account\",\"is_group\",\"=\",0],[\"Account\",\"root_type\",\"=\",\"Income\"]]",
"options": "Account"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-06-28 13:05:14.403852",
"modified_by": "Administrator",
"module": "Education",
"name": "Fee Category Default",
"owner": "Administrator",
"permissions": [],
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

# import frappe
from frappe.model.document import Document


class FeeCategoryDefault(Document):
pass
Loading

0 comments on commit 5e80531

Please sign in to comment.