Compare commits

..

1 Commits

Author SHA1 Message Date
coderabbitai[bot]
b7ae191478 📝 Add docstrings to fix/warehouse-source-reference
Docstrings generation was requested by @matteoarosti.

* https://github.com/frappe/erpnext/pull/50058#issuecomment-3399282664

The following files were modified:

* `erpnext/manufacturing/report/production_planning_report/production_planning_report.py`
2025-10-13 22:40:48 +00:00
452 changed files with 298494 additions and 118326 deletions

View File

@@ -6,7 +6,7 @@ Feature requests are also a great way to take the product forward. New ideas can
When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want.
The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.frappe.io](https://discuss.frappe.io/c/erpnext/6).
The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.erpnext.com](https://discuss.erpnext.com).
### Reply and Closing Policy

View File

@@ -9,7 +9,7 @@ body:
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.frappe.io/c/erpnext/6)
- For questions and general support, checkout the [user manual](https://docs.erpnext.com/) or use [forum](https://discuss.erpnext.com)
- For documentation issues, propose edit on [documentation site](https://docs.erpnext.com/) directly.
2. When making a bug report, make sure you provide all required information. The easier it is for
maintainers to reproduce, the faster it'll be fixed.

View File

@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Community Forum
url: https://discuss.frappe.io/c/erpnext/6
url: https://discuss.erpnext.com/
about: For general QnA, discussions and community help.

View File

@@ -11,7 +11,7 @@ assignees: ''
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the manual https://docs.erpnext.com or use https://discuss.frappe.io/c/erpnext/6
- For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
@@ -21,7 +21,7 @@ Please keep in mind that we get many many requests and we can't possibly work on
If you're in urgent need to a feature, please try the following channels to get paid developments done quickly:
1. Certified ERPNext partners: https://erpnext.com/partners
2. Developer community on ERPNext forums: https://discuss.frappe.io/c/framework/5
2. Developer community on ERPNext forums: https://discuss.erpnext.com/c/developers/5
3. Telegram group for ERPNext/Frappe development work: https://t.me/erpnext_opps
-->

View File

@@ -133,7 +133,7 @@ To setup the repository locally follow the steps mentioned below:
1. [Frappe School](https://school.frappe.io) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext.
3. [Discussion Forum](https://discuss.frappe.io/c/erpnext/6) - Engage with community of ERPNext users and service providers.
3. [Discussion Forum](https://discuss.erpnext.com/) - Engage with community of ERPNext users and service providers.
4. [Telegram Group](https://erpnext_public.t.me) - Get instant help from huge community of users.

View File

@@ -21,7 +21,6 @@
"account_currency",
"column_break1",
"parent_account",
"account_category",
"account_type",
"tax_rate",
"freeze_account",
@@ -190,20 +189,13 @@
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disable"
},
{
"description": "Used with Financial Report Template",
"fieldname": "account_category",
"fieldtype": "Link",
"label": "Account Category",
"options": "Account Category"
}
],
"icon": "fa fa-money",
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2025-08-02 06:26:44.657146",
"modified": "2025-01-22 10:40:35.766017",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",
@@ -258,7 +250,6 @@
"write": 1
}
],
"row_format": "Dynamic",
"search_fields": "account_number",
"show_name_in_global_search": 1,
"show_preview_popup": 1,
@@ -266,4 +257,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -31,7 +31,6 @@ class Account(NestedSet):
if TYPE_CHECKING:
from frappe.types import DF
account_category: DF.Link | None
account_currency: DF.Link | None
account_name: DF.Data
account_number: DF.Data | None
@@ -109,7 +108,6 @@ class Account(NestedSet):
self.validate_parent_child_account_type()
self.validate_root_details()
self.validate_account_number()
self.validate_disabled()
self.validate_group_or_ledger()
self.set_root_and_report_type()
self.validate_mandatory()
@@ -254,14 +252,6 @@ class Account(NestedSet):
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
def validate_disabled(self):
doc_before_save = self.get_doc_before_save()
if not doc_before_save or cint(doc_before_save.disabled) == cint(self.disabled):
return
if cint(self.disabled):
self.validate_default_accounts_in_company()
def validate_group_or_ledger(self):
doc_before_save = self.get_doc_before_save()
if not doc_before_save or cint(doc_before_save.is_group) == cint(self.is_group):
@@ -272,32 +262,9 @@ class Account(NestedSet):
elif cint(self.is_group):
if self.account_type and not self.flags.exclude_account_type_check:
throw(_("Cannot covert to Group because Account Type is selected."))
self.validate_default_accounts_in_company()
elif self.check_if_child_exists():
throw(_("Account with child nodes cannot be set as ledger"))
def validate_default_accounts_in_company(self):
default_account_fields = get_company_default_account_fields()
company_default_accounts = frappe.db.get_value(
"Company", self.company, list(default_account_fields.keys()), as_dict=1
)
msg = _("Account {0} cannot be disabled as it is already set as {1} for {2}.")
if not self.disabled:
msg = _("Account {0} cannot be converted to Group as it is already set as {1} for {2}.")
for d in default_account_fields:
if company_default_accounts.get(d) == self.name:
throw(
msg.format(
frappe.bold(self.name),
frappe.bold(default_account_fields.get(d)),
frappe.bold(self.company),
)
)
def validate_frozen_accounts_modifier(self):
doc_before_save = self.get_doc_before_save()
if not doc_before_save or doc_before_save.freeze_account == self.freeze_account:
@@ -658,27 +625,3 @@ def _ensure_idle_system():
).format(pretty_date(last_gl_update)),
title=_("System In Use"),
)
def get_company_default_account_fields():
return {
"default_bank_account": "Default Bank Account",
"default_cash_account": "Default Cash Account",
"default_receivable_account": "Default Receivable Account",
"default_payable_account": "Default Payable Account",
"default_expense_account": "Default Expense Account",
"default_income_account": "Default Income Account",
"stock_received_but_not_billed": "Stock Received But Not Billed Account",
"stock_adjustment_account": "Stock Adjustment Account",
"write_off_account": "Write Off Account",
"default_discount_account": "Default Payment Discount Account",
"unrealized_profit_loss_account": "Unrealized Profit / Loss Account",
"exchange_gain_loss_account": "Exchange Gain / Loss Account",
"unrealized_exchange_gain_loss_account": "Unrealized Exchange Gain / Loss Account",
"round_off_account": "Round Off Account",
"default_deferred_revenue_account": "Default Deferred Revenue Account",
"default_deferred_expense_account": "Default Deferred Expense Account",
"accumulated_depreciation_account": "Accumulated Depreciation Account",
"depreciation_expense_account": "Depreciation Expense Account",
"disposal_account": "Gain/Loss Account on Asset Disposal",
}

View File

@@ -23,7 +23,15 @@ def create_charts(
if root_account:
root_type = child.get("root_type")
if account_name not in get_chart_metadata_fields():
if account_name not in [
"account_name",
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
"account_currency",
]:
account_number = cstr(child.get("account_number")).strip()
account_name, account_name_in_db = add_suffix_if_duplicate(
account_name, account_number, accounts
@@ -47,7 +55,6 @@ def create_charts(
"report_type": report_type,
"account_number": account_number,
"account_type": child.get("account_type"),
"account_category": child.get("account_category"),
"account_currency": child.get("account_currency")
if custom_chart
else frappe.get_cached_value("Company", company, "default_currency"),
@@ -90,7 +97,20 @@ def add_suffix_if_duplicate(account_name, account_number, accounts):
def identify_is_group(child):
if child.get("is_group"):
is_group = child.get("is_group")
elif len(set(child.keys()) - set(get_chart_metadata_fields())):
elif len(
set(child.keys())
- set(
[
"account_name",
"account_type",
"root_type",
"is_group",
"tax_rate",
"account_number",
"account_currency",
]
)
):
is_group = 1
else:
is_group = 0
@@ -233,7 +253,13 @@ def validate_bank_account(coa, bank_account):
def _get_account_names(account_master):
for account_name, child in account_master.items():
if account_name not in get_chart_metadata_fields():
if account_name not in [
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
]:
accounts.append(account_name)
_get_account_names(child)
@@ -258,7 +284,15 @@ def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=Fals
"""recursively called to form a parent-child based list of dict from chart template"""
for account_name, child in children.items():
account = {}
if account_name in get_chart_metadata_fields():
if account_name in [
"account_name",
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
"account_currency",
]:
continue
if from_coa_importer:
@@ -276,16 +310,3 @@ def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=Fals
_import_accounts(chart, None)
return accounts
def get_chart_metadata_fields():
return [
"account_name",
"account_number",
"account_type",
"account_category",
"root_type",
"is_group",
"tax_rate",
"account_currency",
]

View File

@@ -9,192 +9,103 @@ def get():
return {
_("Application of Funds (Assets)"): {
_("Current Assets"): {
_("Accounts Receivable"): {
_("Debtors"): {"account_type": "Receivable", "account_category": "Trade Receivables"}
},
_("Bank Accounts"): {
"account_type": "Bank",
"is_group": 1,
"account_category": "Cash and Cash Equivalents",
},
_("Cash In Hand"): {
_("Cash"): {"account_type": "Cash", "account_category": "Cash and Cash Equivalents"},
"account_type": "Cash",
"account_category": "Cash and Cash Equivalents",
},
_("Accounts Receivable"): {_("Debtors"): {"account_type": "Receivable"}},
_("Bank Accounts"): {"account_type": "Bank", "is_group": 1},
_("Cash In Hand"): {_("Cash"): {"account_type": "Cash"}, "account_type": "Cash"},
_("Loans and Advances (Assets)"): {
_("Employee Advances"): {
"account_type": "Payable",
"account_category": "Other Receivables",
},
_("Employee Advances"): {"account_type": "Payable"},
},
_("Securities and Deposits"): {
_("Earnest Money"): {"account_category": "Other Current Assets"}
},
_("Prepaid Expenses"): {"account_category": "Other Current Assets"},
_("Short-term Investments"): {"account_category": "Short-term Investments"},
_("Securities and Deposits"): {_("Earnest Money"): {}},
_("Stock Assets"): {
_("Stock In Hand"): {"account_type": "Stock", "account_category": "Stock Assets"},
_("Stock In Hand"): {"account_type": "Stock"},
"account_type": "Stock",
"account_category": "Stock Assets",
},
_("Tax Assets"): {"is_group": 1, "account_category": "Other Current Assets"},
_("Tax Assets"): {"is_group": 1},
},
_("Fixed Assets"): {
_("Capital Equipment"): {
"account_type": "Fixed Asset",
"account_category": "Tangible Assets",
},
_("Electronic Equipment"): {
"account_type": "Fixed Asset",
"account_category": "Tangible Assets",
},
_("Furniture and Fixtures"): {
"account_type": "Fixed Asset",
"account_category": "Tangible Assets",
},
_("Office Equipment"): {"account_type": "Fixed Asset", "account_category": "Tangible Assets"},
_("Plants and Machineries"): {
"account_type": "Fixed Asset",
"account_category": "Tangible Assets",
},
_("Buildings"): {"account_type": "Fixed Asset", "account_category": "Tangible Assets"},
_("Software"): {"account_type": "Fixed Asset", "account_category": "Intangible Assets"},
_("Accumulated Depreciation"): {
"account_type": "Accumulated Depreciation",
"account_category": "Tangible Assets",
},
_("Capital Equipment"): {"account_type": "Fixed Asset"},
_("Electronic Equipment"): {"account_type": "Fixed Asset"},
_("Furniture and Fixtures"): {"account_type": "Fixed Asset"},
_("Office Equipment"): {"account_type": "Fixed Asset"},
_("Plants and Machineries"): {"account_type": "Fixed Asset"},
_("Buildings"): {"account_type": "Fixed Asset"},
_("Software"): {"account_type": "Fixed Asset"},
_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
_("CWIP Account"): {
"account_type": "Capital Work in Progress",
"account_category": "Tangible Assets",
},
},
_("Investments"): {"is_group": 1, "account_category": "Long-term Investments"},
_("Temporary Accounts"): {
_("Temporary Opening"): {
"account_type": "Temporary",
"account_category": "Other Non-current Assets",
}
},
_("Investments"): {"is_group": 1},
_("Temporary Accounts"): {_("Temporary Opening"): {"account_type": "Temporary"}},
"root_type": "Asset",
},
_("Expenses"): {
_("Direct Expenses"): {
_("Stock Expenses"): {
_("Cost of Goods Sold"): {
"account_type": "Cost of Goods Sold",
"account_category": "Cost of Goods Sold",
},
_("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold"},
_("Expenses Included In Asset Valuation"): {
"account_type": "Expenses Included In Asset Valuation",
"account_category": "Other Direct Costs",
},
_("Expenses Included In Valuation"): {
"account_type": "Expenses Included In Valuation",
"account_category": "Other Direct Costs",
},
_("Stock Adjustment"): {
"account_type": "Stock Adjustment",
"account_category": "Other Direct Costs",
"account_type": "Expenses Included In Asset Valuation"
},
_("Expenses Included In Valuation"): {"account_type": "Expenses Included In Valuation"},
_("Stock Adjustment"): {"account_type": "Stock Adjustment"},
},
},
_("Indirect Expenses"): {
_("Administrative Expenses"): {"account_category": "Operating Expenses"},
_("Commission on Sales"): {"account_category": "Operating Expenses"},
_("Depreciation"): {"account_type": "Depreciation", "account_category": "Operating Expenses"},
_("Entertainment Expenses"): {"account_category": "Operating Expenses"},
_("Freight and Forwarding Charges"): {
"account_type": "Chargeable",
"account_category": "Operating Expenses",
},
_("Legal Expenses"): {"account_category": "Operating Expenses"},
_("Marketing Expenses"): {
"account_type": "Chargeable",
"account_category": "Operating Expenses",
},
_("Miscellaneous Expenses"): {
"account_type": "Chargeable",
"account_category": "Operating Expenses",
},
_("Office Maintenance Expenses"): {"account_category": "Operating Expenses"},
_("Office Rent"): {"account_category": "Operating Expenses"},
_("Postal Expenses"): {"account_category": "Operating Expenses"},
_("Print and Stationery"): {"account_category": "Operating Expenses"},
_("Round Off"): {"account_type": "Round Off", "account_category": "Operating Expenses"},
_("Salary"): {"account_category": "Operating Expenses"},
_("Sales Expenses"): {"account_category": "Operating Expenses"},
_("Telephone Expenses"): {"account_category": "Operating Expenses"},
_("Travel Expenses"): {"account_category": "Operating Expenses"},
_("Utility Expenses"): {"account_category": "Operating Expenses"},
_("Write Off"): {"account_category": "Operating Expenses"},
_("Exchange Gain/Loss"): {"account_category": "Operating Expenses"},
_("Interest Expense"): {"account_category": "Finance Costs"},
_("Bank Charges"): {"account_category": "Finance Costs"},
_("Gain/Loss on Asset Disposal"): {"account_category": "Other Operating Income"},
_("Impairment"): {"account_category": "Operating Expenses"},
_("Tax Expense"): {"account_category": "Tax Expense"},
_("Administrative Expenses"): {},
_("Commission on Sales"): {},
_("Depreciation"): {"account_type": "Depreciation"},
_("Entertainment Expenses"): {},
_("Freight and Forwarding Charges"): {"account_type": "Chargeable"},
_("Legal Expenses"): {},
_("Marketing Expenses"): {"account_type": "Chargeable"},
_("Miscellaneous Expenses"): {"account_type": "Chargeable"},
_("Office Maintenance Expenses"): {},
_("Office Rent"): {},
_("Postal Expenses"): {},
_("Print and Stationery"): {},
_("Round Off"): {"account_type": "Round Off"},
_("Salary"): {},
_("Sales Expenses"): {},
_("Telephone Expenses"): {},
_("Travel Expenses"): {},
_("Utility Expenses"): {},
_("Write Off"): {},
_("Exchange Gain/Loss"): {},
_("Gain/Loss on Asset Disposal"): {},
_("Impairment"): {},
},
"root_type": "Expense",
},
_("Income"): {
_("Direct Income"): {
_("Sales"): {"account_category": "Revenue from Operations"},
_("Service"): {"account_category": "Revenue from Operations"},
},
_("Indirect Income"): {
_("Interest Income"): {"account_category": "Investment Income"},
_("Interest on Fixed Deposits"): {"account_category": "Investment Income"},
"is_group": 1,
},
_("Direct Income"): {_("Sales"): {}, _("Service"): {}},
_("Indirect Income"): {"is_group": 1},
"root_type": "Income",
},
_("Source of Funds (Liabilities)"): {
_("Current Liabilities"): {
_("Accounts Payable"): {
_("Creditors"): {"account_type": "Payable", "account_category": "Trade Payables"},
_("Payroll Payable"): {"account_category": "Other Payables"},
_("Creditors"): {"account_type": "Payable"},
_("Payroll Payable"): {},
},
_("Accrued Expenses"): {"account_category": "Other Current Liabilities"},
_("Customer Advances"): {"account_category": "Other Current Liabilities"},
_("Stock Liabilities"): {
_("Stock Received But Not Billed"): {
"account_type": "Stock Received But Not Billed",
"account_category": "Trade Payables",
},
_("Asset Received But Not Billed"): {
"account_type": "Asset Received But Not Billed",
"account_category": "Trade Payables",
},
_("Stock Received But Not Billed"): {"account_type": "Stock Received But Not Billed"},
_("Asset Received But Not Billed"): {"account_type": "Asset Received But Not Billed"},
},
_("Duties and Taxes"): {
"account_type": "Tax",
"is_group": 1,
"account_category": "Current Tax Liabilities",
},
_("Short-term Provisions"): {"account_category": "Short-term Provisions"},
_("Duties and Taxes"): {"account_type": "Tax", "is_group": 1},
_("Loans (Liabilities)"): {
_("Secured Loans"): {"account_category": "Long-term Borrowings"},
_("Unsecured Loans"): {"account_category": "Long-term Borrowings"},
_("Bank Overdraft Account"): {"account_category": "Short-term Borrowings"},
_("Secured Loans"): {},
_("Unsecured Loans"): {},
_("Bank Overdraft Account"): {},
},
},
_("Non-Current Liabilities"): {
_("Long-term Provisions"): {"account_category": "Long-term Provisions"},
_("Employee Benefits Obligation"): {"account_category": "Other Non-current Liabilities"},
"is_group": 1,
},
"root_type": "Liability",
},
_("Equity"): {
_("Capital Stock"): {"account_type": "Equity", "account_category": "Share Capital"},
_("Dividends Paid"): {"account_type": "Equity", "account_category": "Reserves and Surplus"},
_("Opening Balance Equity"): {
"account_type": "Equity",
"account_category": "Reserves and Surplus",
},
_("Retained Earnings"): {"account_type": "Equity", "account_category": "Reserves and Surplus"},
_("Revaluation Surplus"): {"account_type": "Equity", "account_category": "Reserves and Surplus"},
_("Capital Stock"): {"account_type": "Equity"},
_("Dividends Paid"): {"account_type": "Equity"},
_("Opening Balance Equity"): {"account_type": "Equity"},
_("Retained Earnings"): {"account_type": "Equity"},
_("Revaluation Surplus"): {"account_type": "Equity"},
"root_type": "Equity",
},
}

View File

@@ -10,128 +10,49 @@ def get():
_("Application of Funds (Assets)"): {
_("Current Assets"): {
_("Accounts Receivable"): {
_("Debtors"): {
"account_type": "Receivable",
"account_number": "1310",
"account_category": "Trade Receivables",
},
_("Debtors"): {"account_type": "Receivable", "account_number": "1310"},
"account_number": "1300",
},
_("Bank Accounts"): {
"account_type": "Bank",
"is_group": 1,
"account_number": "1200",
"account_category": "Cash and Cash Equivalents",
},
_("Bank Accounts"): {"account_type": "Bank", "is_group": 1, "account_number": "1200"},
_("Cash In Hand"): {
_("Cash"): {
"account_type": "Cash",
"account_number": "1110",
"account_category": "Cash and Cash Equivalents",
},
_("Cash"): {"account_type": "Cash", "account_number": "1110"},
"account_type": "Cash",
"account_number": "1100",
"account_category": "Cash and Cash Equivalents",
},
_("Loans and Advances (Assets)"): {
_("Employee Advances"): {
"account_number": "1610",
"account_type": "Payable",
"account_category": "Other Receivables",
},
_("Employee Advances"): {"account_number": "1610", "account_type": "Payable"},
"account_number": "1600",
},
_("Securities and Deposits"): {
_("Earnest Money"): {
"account_number": "1651",
"account_category": "Other Current Assets",
},
_("Earnest Money"): {"account_number": "1651"},
"account_number": "1650",
},
_("Prepaid Expenses"): {
"account_number": "1660",
"account_category": "Other Current Assets",
},
_("Short-term Investments"): {
"account_number": "1670",
"account_category": "Short-term Investments",
},
_("Stock Assets"): {
_("Stock In Hand"): {
"account_type": "Stock",
"account_number": "1410",
"account_category": "Stock Assets",
},
_("Stock In Hand"): {"account_type": "Stock", "account_number": "1410"},
"account_type": "Stock",
"account_number": "1400",
"account_category": "Stock Assets",
},
_("Tax Assets"): {
"is_group": 1,
"account_number": "1500",
"account_category": "Other Current Assets",
},
_("Tax Assets"): {"is_group": 1, "account_number": "1500"},
"account_number": "1100-1600",
},
_("Fixed Assets"): {
_("Capital Equipment"): {
"account_type": "Fixed Asset",
"account_number": "1710",
"account_category": "Tangible Assets",
},
_("Electronic Equipment"): {
"account_type": "Fixed Asset",
"account_number": "1720",
"account_category": "Tangible Assets",
},
_("Furniture and Fixtures"): {
"account_type": "Fixed Asset",
"account_number": "1730",
"account_category": "Tangible Assets",
},
_("Office Equipment"): {
"account_type": "Fixed Asset",
"account_number": "1740",
"account_category": "Tangible Assets",
},
_("Plants and Machineries"): {
"account_type": "Fixed Asset",
"account_number": "1750",
"account_category": "Tangible Assets",
},
_("Buildings"): {
"account_type": "Fixed Asset",
"account_number": "1760",
"account_category": "Tangible Assets",
},
_("Software"): {
"account_type": "Fixed Asset",
"account_number": "1770",
"account_category": "Intangible Assets",
},
_("Capital Equipment"): {"account_type": "Fixed Asset", "account_number": "1710"},
_("Electronic Equipment"): {"account_type": "Fixed Asset", "account_number": "1720"},
_("Furniture and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
_("Office Equipment"): {"account_type": "Fixed Asset", "account_number": "1740"},
_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
_("Software"): {"account_type": "Fixed Asset", "account_number": "1770"},
_("Accumulated Depreciation"): {
"account_type": "Accumulated Depreciation",
"account_number": "1780",
"account_category": "Tangible Assets",
},
_("CWIP Account"): {
"account_type": "Capital Work in Progress",
"account_number": "1790",
"account_category": "Tangible Assets",
},
_("CWIP Account"): {"account_type": "Capital Work in Progress", "account_number": "1790"},
"account_number": "1700",
},
_("Investments"): {
"is_group": 1,
"account_number": "1800",
"account_category": "Long-term Investments",
},
_("Investments"): {"is_group": 1, "account_number": "1800"},
_("Temporary Accounts"): {
_("Temporary Opening"): {
"account_type": "Temporary",
"account_number": "1910",
"account_category": "Other Non-current Assets",
},
_("Temporary Opening"): {"account_type": "Temporary", "account_number": "1910"},
"account_number": "1900",
},
"root_type": "Asset",
@@ -140,220 +61,97 @@ def get():
_("Expenses"): {
_("Direct Expenses"): {
_("Stock Expenses"): {
_("Cost of Goods Sold"): {
"account_type": "Cost of Goods Sold",
"account_number": "5111",
"account_category": "Cost of Goods Sold",
},
_("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold", "account_number": "5111"},
_("Expenses Included In Asset Valuation"): {
"account_type": "Expenses Included In Asset Valuation",
"account_number": "5112",
"account_category": "Other Direct Costs",
},
_("Expenses Included In Valuation"): {
"account_type": "Expenses Included In Valuation",
"account_number": "5118",
"account_category": "Other Direct Costs",
},
_("Stock Adjustment"): {
"account_type": "Stock Adjustment",
"account_number": "5119",
"account_category": "Other Direct Costs",
},
_("Stock Adjustment"): {"account_type": "Stock Adjustment", "account_number": "5119"},
"account_number": "5110",
},
"account_number": "5100",
},
_("Indirect Expenses"): {
_("Administrative Expenses"): {
"account_number": "5201",
"account_category": "Operating Expenses",
},
_("Commission on Sales"): {
"account_number": "5202",
"account_category": "Operating Expenses",
},
_("Depreciation"): {
"account_type": "Depreciation",
"account_number": "5203",
"account_category": "Operating Expenses",
},
_("Entertainment Expenses"): {
"account_number": "5204",
"account_category": "Operating Expenses",
},
_("Freight and Forwarding Charges"): {
"account_type": "Chargeable",
"account_number": "5205",
"account_category": "Operating Expenses",
},
_("Legal Expenses"): {"account_number": "5206", "account_category": "Operating Expenses"},
_("Marketing Expenses"): {
"account_type": "Chargeable",
"account_number": "5207",
"account_category": "Operating Expenses",
},
_("Office Maintenance Expenses"): {
"account_number": "5208",
"account_category": "Operating Expenses",
},
_("Office Rent"): {"account_number": "5209", "account_category": "Operating Expenses"},
_("Postal Expenses"): {"account_number": "5210", "account_category": "Operating Expenses"},
_("Print and Stationery"): {
"account_number": "5211",
"account_category": "Operating Expenses",
},
_("Round Off"): {
"account_type": "Round Off",
"account_number": "5212",
"account_category": "Operating Expenses",
},
_("Salary"): {"account_number": "5213", "account_category": "Operating Expenses"},
_("Sales Expenses"): {"account_number": "5214", "account_category": "Operating Expenses"},
_("Telephone Expenses"): {"account_number": "5215", "account_category": "Operating Expenses"},
_("Travel Expenses"): {"account_number": "5216", "account_category": "Operating Expenses"},
_("Utility Expenses"): {"account_number": "5217", "account_category": "Operating Expenses"},
_("Write Off"): {"account_number": "5218", "account_category": "Operating Expenses"},
_("Exchange Gain/Loss"): {"account_number": "5219", "account_category": "Operating Expenses"},
_("Interest Expense"): {"account_number": "5220", "account_category": "Finance Costs"},
_("Bank Charges"): {"account_number": "5221", "account_category": "Finance Costs"},
_("Gain/Loss on Asset Disposal"): {
"account_number": "5222",
"account_category": "Other Operating Income",
},
_("Miscellaneous Expenses"): {
"account_type": "Chargeable",
"account_number": "5223",
"account_category": "Operating Expenses",
},
_("Impairment"): {"account_number": "5224", "account_category": "Operating Expenses"},
_("Tax Expense"): {"account_number": "5225", "account_category": "Tax Expense"},
_("Administrative Expenses"): {"account_number": "5201"},
_("Commission on Sales"): {"account_number": "5202"},
_("Depreciation"): {"account_type": "Depreciation", "account_number": "5203"},
_("Entertainment Expenses"): {"account_number": "5204"},
_("Freight and Forwarding Charges"): {"account_type": "Chargeable", "account_number": "5205"},
_("Legal Expenses"): {"account_number": "5206"},
_("Marketing Expenses"): {"account_type": "Chargeable", "account_number": "5207"},
_("Office Maintenance Expenses"): {"account_number": "5208"},
_("Office Rent"): {"account_number": "5209"},
_("Postal Expenses"): {"account_number": "5210"},
_("Print and Stationery"): {"account_number": "5211"},
_("Round Off"): {"account_type": "Round Off", "account_number": "5212"},
_("Salary"): {"account_number": "5213"},
_("Sales Expenses"): {"account_number": "5214"},
_("Telephone Expenses"): {"account_number": "5215"},
_("Travel Expenses"): {"account_number": "5216"},
_("Utility Expenses"): {"account_number": "5217"},
_("Write Off"): {"account_number": "5218"},
_("Exchange Gain/Loss"): {"account_number": "5219"},
_("Gain/Loss on Asset Disposal"): {"account_number": "5220"},
_("Miscellaneous Expenses"): {"account_type": "Chargeable", "account_number": "5221"},
"account_number": "5200",
},
"root_type": "Expense",
"account_number": "5000",
},
_("Income"): {
_("Direct Income"): {
_("Sales"): {"account_number": "4110", "account_category": "Revenue from Operations"},
_("Service"): {"account_number": "4120", "account_category": "Revenue from Operations"},
_("Sales"): {"account_number": "4110"},
_("Service"): {"account_number": "4120"},
"account_number": "4100",
},
_("Indirect Income"): {
_("Interest Income"): {"account_number": "4210", "account_category": "Investment Income"},
_("Interest on Fixed Deposits"): {
"account_number": "4220",
"account_category": "Investment Income",
},
"is_group": 1,
"account_number": "4200",
},
_("Indirect Income"): {"is_group": 1, "account_number": "4200"},
"root_type": "Income",
"account_number": "4000",
},
_("Source of Funds (Liabilities)"): {
_("Current Liabilities"): {
_("Accounts Payable"): {
_("Creditors"): {
"account_type": "Payable",
"account_number": "2110",
"account_category": "Trade Payables",
},
_("Payroll Payable"): {"account_number": "2120", "account_category": "Other Payables"},
_("Creditors"): {"account_type": "Payable", "account_number": "2110"},
_("Payroll Payable"): {"account_number": "2120"},
"account_number": "2100",
},
_("Accrued Expenses"): {
"account_number": "2150",
"account_category": "Other Current Liabilities",
},
_("Customer Advances"): {
"account_number": "2160",
"account_category": "Other Current Liabilities",
},
_("Stock Liabilities"): {
_("Stock Received But Not Billed"): {
"account_type": "Stock Received But Not Billed",
"account_number": "2210",
"account_category": "Trade Payables",
},
_("Asset Received But Not Billed"): {
"account_type": "Asset Received But Not Billed",
"account_number": "2211",
"account_category": "Trade Payables",
},
"account_number": "2200",
},
_("Duties and Taxes"): {
_("TDS Payable"): {
"account_number": "2310",
"account_category": "Current Tax Liabilities",
},
_("TDS Payable"): {"account_number": "2310"},
"account_type": "Tax",
"is_group": 1,
"account_number": "2300",
"account_category": "Current Tax Liabilities",
},
_("Short-term Provisions"): {
"account_number": "2350",
"account_category": "Short-term Provisions",
},
_("Loans (Liabilities)"): {
_("Secured Loans"): {
"account_number": "2410",
"account_category": "Long-term Borrowings",
},
_("Unsecured Loans"): {
"account_number": "2420",
"account_category": "Long-term Borrowings",
},
_("Bank Overdraft Account"): {
"account_number": "2430",
"account_category": "Short-term Borrowings",
},
_("Secured Loans"): {"account_number": "2410"},
_("Unsecured Loans"): {"account_number": "2420"},
_("Bank Overdraft Account"): {"account_number": "2430"},
"account_number": "2400",
},
"account_number": "2100-2400",
},
_("Non-Current Liabilities"): {
_("Long-term Provisions"): {
"account_number": "2510",
"account_category": "Long-term Provisions",
},
_("Employee Benefits Obligation"): {
"account_number": "2520",
"account_category": "Other Non-current Liabilities",
},
"is_group": 1,
"account_number": "2500",
},
"root_type": "Liability",
"account_number": "2000",
},
_("Equity"): {
_("Capital Stock"): {
"account_type": "Equity",
"account_number": "3100",
"account_category": "Share Capital",
},
_("Dividends Paid"): {
"account_type": "Equity",
"account_number": "3200",
"account_category": "Reserves and Surplus",
},
_("Opening Balance Equity"): {
"account_type": "Equity",
"account_number": "3300",
"account_category": "Reserves and Surplus",
},
_("Retained Earnings"): {
"account_type": "Equity",
"account_number": "3400",
"account_category": "Reserves and Surplus",
},
_("Revaluation Surplus"): {
"account_type": "Equity",
"account_number": "3500",
"account_category": "Reserves and Surplus",
},
_("Capital Stock"): {"account_type": "Equity", "account_number": "3100"},
_("Dividends Paid"): {"account_type": "Equity", "account_number": "3200"},
_("Opening Balance Equity"): {"account_type": "Equity", "account_number": "3300"},
_("Retained Earnings"): {"account_type": "Equity", "account_number": "3400"},
"root_type": "Equity",
"account_number": "3000",
},

View File

@@ -1,8 +0,0 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Account Category", {
// refresh(frm) {
// },
// });

View File

@@ -1,71 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:account_category_name",
"creation": "2025-08-02 06:22:31.835063",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"account_category_name",
"description"
],
"fields": [
{
"fieldname": "account_category_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Account Category Name",
"reqd": 1,
"unique": 1
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-10-15 03:19:47.171349",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Category",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"read": 1,
"role": "Auditor"
}
],
"row_format": "Dynamic",
"search_fields": "account_category_name, description",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,94 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import json
import os
import frappe
from frappe import _
from frappe.model.document import Document, bulk_insert
DOCTYPE = "Account Category"
class AccountCategory(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
account_category_name: DF.Data
description: DF.SmallText | None
# end: auto-generated types
def after_rename(self, old_name, new_name, merge):
from erpnext.accounts.doctype.financial_report_template.financial_report_engine import (
FormulaFieldUpdater,
)
# get all template rows with this account category being used
row = frappe.qb.DocType("Financial Report Row")
rows = frappe._dict(
frappe.qb.from_(row)
.select(row.name, row.calculation_formula)
.where(row.calculation_formula.like(f"%{old_name}%"))
.run()
)
if not rows:
return
# Update formulas with new name
updater = FormulaFieldUpdater(
field_name="account_category",
value_mapping={old_name: new_name},
exclude_operators=["like", "not like"],
)
updated_formulas = updater.update_in_rows(rows)
if updated_formulas:
frappe.msgprint(
_("Updated {0} Financial Report Row(s) with new category name").format(len(updated_formulas))
)
def import_account_categories(template_path: str):
categories_file = os.path.join(template_path, "account_categories.json")
if not os.path.exists(categories_file):
return
with open(categories_file) as f:
categories = json.load(f, object_hook=frappe._dict)
create_account_categories(categories)
def create_account_categories(categories: list[dict]):
if not categories:
return
existing_categories = set(frappe.get_all(DOCTYPE, pluck="name"))
new_categories = []
for category_data in categories:
category_name = category_data.get("account_category_name")
if not category_name or category_name in existing_categories:
continue
doc = frappe.get_doc(
{
**category_data,
"doctype": DOCTYPE,
"name": category_name,
}
)
new_categories.append(doc)
existing_categories.add(category_name)
if new_categories:
bulk_insert(DOCTYPE, new_categories)

View File

@@ -1,20 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests import IntegrationTestCase
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class IntegrationTestAccountCategory(IntegrationTestCase):
"""
Integration tests for AccountCategory.
Use this class for testing interactions between multiple components.
"""
pass

View File

@@ -309,8 +309,8 @@ def get_dimensions(with_cost_center_and_project=False):
if with_cost_center_and_project:
dimension_filters.extend(
[
frappe._dict({"fieldname": "cost_center", "document_type": "Cost Center"}),
frappe._dict({"fieldname": "project", "document_type": "Project"}),
{"fieldname": "cost_center", "document_type": "Cost Center"},
{"fieldname": "project", "document_type": "Project"},
]
)

View File

@@ -77,7 +77,6 @@
"period_closing_settings_section",
"acc_frozen_upto",
"ignore_account_closing_balance",
"use_legacy_controller_for_pcv",
"column_break_25",
"frozen_accounts_modifier",
"tab_break_dpet",
@@ -652,12 +651,6 @@
"fieldname": "use_legacy_budget_controller",
"fieldtype": "Check",
"label": "Use Legacy Budget Controller"
},
{
"default": "1",
"fieldname": "use_legacy_controller_for_pcv",
"fieldtype": "Check",
"label": "Use Legacy Controller For Period Closing Voucher"
}
],
"grid_page_length": 50,
@@ -666,7 +659,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2025-10-20 14:06:08.870427",
"modified": "2025-09-24 16:08:08.515254",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -75,7 +75,6 @@ class AccountsSettings(Document):
unlink_advance_payment_on_cancelation_of_order: DF.Check
unlink_payment_on_cancellation_of_invoice: DF.Check
use_legacy_budget_controller: DF.Check
use_legacy_controller_for_pcv: DF.Check
# end: auto-generated types
def validate(self):

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Advance Payment Ledger Entry", {
refresh(frm) {
frm.set_currency_labels(["amount"], frm.doc.currency);
frm.set_currency_labels(["base_amount"], erpnext.get_currency(frm.doc.company));
},
});
// frappe.ui.form.on("Advance Payment Ledger Entry", {
// refresh(frm) {
// },
// });

View File

@@ -10,10 +10,8 @@
"voucher_no",
"against_voucher_type",
"against_voucher_no",
"currency",
"exchange_rate",
"amount",
"base_amount",
"currency",
"event",
"delinked"
],
@@ -78,29 +76,13 @@
"fieldtype": "Check",
"label": "DeLinked",
"read_only": 1
},
{
"depends_on": "base_amount",
"fieldname": "base_amount",
"fieldtype": "Currency",
"label": "Amount (Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"depends_on": "exchange_rate",
"fieldname": "exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"precision": "9",
"read_only": 1
}
],
"grid_page_length": 50,
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-11-13 12:45:03.014555",
"modified": "2025-10-13 15:11:58.300836",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Advance Payment Ledger Entry",

View File

@@ -19,12 +19,10 @@ class AdvancePaymentLedgerEntry(Document):
against_voucher_no: DF.DynamicLink | None
against_voucher_type: DF.Link | None
amount: DF.Currency
base_amount: DF.Currency
company: DF.Link | None
currency: DF.Link | None
delinked: DF.Check
event: DF.Data | None
exchange_rate: DF.Float
voucher_no: DF.DynamicLink | None
voucher_type: DF.Link | None
# end: auto-generated types

View File

@@ -9,6 +9,13 @@ cur_frm.add_fetch("bank", "swift_number", "swift_number");
frappe.ui.form.on("Bank Guarantee", {
setup: function (frm) {
frm.set_query("bank", function () {
return {
filters: {
company: frm.doc.company,
},
};
});
frm.set_query("bank_account", function () {
return {
filters: {

View File

@@ -123,7 +123,8 @@
"fieldname": "transaction_id",
"fieldtype": "Data",
"label": "Transaction ID",
"read_only": 1
"read_only": 1,
"unique": 1
},
{
"allow_on_submit": 1,
@@ -238,7 +239,7 @@
"grid_page_length": 50,
"is_submittable": 1,
"links": [],
"modified": "2025-10-23 17:32:58.514807",
"modified": "2025-09-26 17:06:29.207673",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",

View File

@@ -272,19 +272,21 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
if total_expense > budget_amount:
if args.actual_expense > budget_amount:
error_tense = _("is already")
diff = args.actual_expense - budget_amount
_msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It is already exceeded by {5}.")
else:
error_tense = _("will be")
diff = total_expense - budget_amount
_msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will be exceeded by {5}.")
currency = frappe.get_cached_value("Company", args.company, "default_currency")
msg = _msg.format(
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It {5} exceed by {6}").format(
_(action_for),
frappe.bold(args.account),
frappe.unscrub(args.budget_against_field),
frappe.bold(budget_against),
frappe.bold(fmt_money(budget_amount, currency=currency)),
error_tense,
frappe.bold(fmt_money(diff, currency=currency)),
)

View File

@@ -1,187 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2025-09-06 09:39:46.503678",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"reference_code",
"display_name",
"indentation_level",
"data_source",
"balance_type",
"column_break_hxqu",
"fieldtype",
"color",
"bold_text",
"italic_text",
"hidden_calculation",
"hide_when_empty",
"reverse_sign",
"include_in_charts",
"section_break_ornw",
"column_break_asfe",
"advanced_filtering",
"filters_editor",
"calculation_formula",
"section_break_pvro",
"formula_description"
],
"fields": [
{
"columns": 1,
"description": "Code to reference this line in formulas (e.g., REV100, EXP200, ASSET100)",
"fieldname": "reference_code",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Line Reference"
},
{
"description": "Text displayed on the financial statement (e.g., 'Total Revenue', 'Cash and Cash Equivalents')",
"fieldname": "display_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Display Name"
},
{
"columns": 1,
"description": "Indentation level: 0 = Main heading, 1 = Sub-category, 2 = Individual accounts, etc.",
"fieldname": "indentation_level",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Indent Level"
},
{
"description": "How this line gets its data",
"fieldname": "data_source",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Data Source",
"options": "\nAccount Data\nCalculated Amount\nCustom API\nBlank Line\nColumn Break\nSection Break"
},
{
"depends_on": "eval:doc.data_source == 'Account Data'",
"description": "Opening Balance = Start of period, Closing Balance = End of period, Period Movement = Net change during period",
"fieldname": "balance_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Balance Type",
"mandatory_depends_on": "eval:doc.data_source == 'Account Data'",
"options": "\nOpening Balance\nClosing Balance\nPeriod Movement (Debits - Credits)"
},
{
"fieldname": "column_break_hxqu",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Bold text for emphasis (totals, major headings)",
"fieldname": "bold_text",
"fieldtype": "Check",
"label": "Bold Text"
},
{
"default": "0",
"description": "Italic text for subtotals or notes",
"fieldname": "italic_text",
"fieldtype": "Check",
"label": "Italic Text"
},
{
"default": "0",
"description": "Calculate but don't show on final report",
"fieldname": "hidden_calculation",
"fieldtype": "Check",
"label": "Hidden Line (Internal Use Only)"
},
{
"default": "0",
"description": "Hide this line if amount is zero",
"fieldname": "hide_when_empty",
"fieldtype": "Check",
"label": "Hide If Zero"
},
{
"columns": 1,
"default": "0",
"description": "Show negative values as positive (for expenses in P&L)",
"fieldname": "reverse_sign",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Reverse Sign"
},
{
"fieldname": "section_break_ornw",
"fieldtype": "Section Break"
},
{
"depends_on": "eval: (doc.data_source === \"Account Data\" && doc.advanced_filtering) || [\"Calculated Amount\", \"Custom API\"].includes(doc.data_source);\n",
"fieldname": "calculation_formula",
"fieldtype": "Code",
"label": "Formula or Account Filter",
"mandatory_depends_on": "eval:doc.data_source != 'Blank Line' && doc.data_source != 'Column Break' && doc.data_source != 'Section Break'"
},
{
"fieldname": "formula_description",
"fieldtype": "HTML"
},
{
"default": "0",
"description": "If enabled, this row's values will be displayed on financial charts",
"fieldname": "include_in_charts",
"fieldtype": "Check",
"label": "Include in Charts"
},
{
"description": "Color to highlight values (e.g., red for exceptions)",
"fieldname": "color",
"fieldtype": "Color",
"label": "Color"
},
{
"description": "How to format and present values in the financial report (only if different from column fieldtype)",
"fieldname": "fieldtype",
"fieldtype": "Select",
"label": "Value Type",
"options": "\nCurrency\nFloat\nInt\nPercent"
},
{
"depends_on": "eval: doc.data_source === \"Account Data\" && !doc.advanced_filtering",
"fieldname": "filters_editor",
"fieldtype": "HTML"
},
{
"depends_on": "eval: ![\"Blank Line\", \"Column Break\", \"Section Break\"].includes(doc.data_source);",
"fieldname": "column_break_asfe",
"fieldtype": "Column Break"
},
{
"default": "0",
"depends_on": "eval: doc.data_source === \"Account Data\"",
"description": "Use <strong>Python</strong> filters to get Accounts",
"fieldname": "advanced_filtering",
"fieldtype": "Check",
"label": "Advanced Filtering",
"print_hide": 1
},
{
"fieldname": "section_break_pvro",
"fieldtype": "Section Break"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-10-14 09:23:27.208072",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Financial Report Row",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,47 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class FinancialReportRow(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
advanced_filtering: DF.Check
balance_type: DF.Literal[
"", "Opening Balance", "Closing Balance", "Period Movement (Debits - Credits)"
]
bold_text: DF.Check
calculation_formula: DF.Code | None
color: DF.Color | None
data_source: DF.Literal[
"",
"Account Data",
"Calculated Amount",
"Custom API",
"Blank Line",
"Column Break",
"Section Break",
]
display_name: DF.Data | None
fieldtype: DF.Literal["", "Currency", "Float", "Int", "Percent"]
hidden_calculation: DF.Check
hide_when_empty: DF.Check
include_in_charts: DF.Check
indentation_level: DF.Int
italic_text: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
reference_code: DF.Data | None
reverse_sign: DF.Check
# end: auto-generated types
pass

View File

@@ -1,433 +0,0 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Financial Report Template", {
refresh(frm) {
// add custom button to view missed accounts
frm.add_custom_button(__("View Account Coverage"), function () {
let selected_rows = frm.get_field("rows").grid.get_selected_children();
const has_selection = selected_rows.length > 0;
if (selected_rows.length === 0) selected_rows = frm.doc.rows;
show_accounts_tree(selected_rows, has_selection);
});
// add custom button to open the financial report
frm.add_custom_button(__("View Report"), function () {
frappe.set_route("query-report", frm.doc.report_type, {
report_template: frm.doc.name,
});
});
},
validate(frm) {
if (!frm.doc.rows || frm.doc.rows.length === 0) {
frappe.msgprint(__("At least one row is required for a financial report template"));
}
},
});
frappe.ui.form.on("Financial Report Row", {
data_source(frm, cdt, cdn) {
const row = locals[cdt][cdn];
update_formula_label(frm, row.data_source);
update_formula_description(frm, row.data_source);
if (row.data_source !== "Account Data") {
frappe.model.set_value(cdt, cdn, "balance_type", "");
}
if (["Blank Line", "Column Break", "Section Break"].includes(row.data_source)) {
frappe.model.set_value(cdt, cdn, "calculation_formula", "");
}
set_up_filters_editor(frm, cdt, cdn);
},
form_render(frm, cdt, cdn) {
const row = locals[cdt][cdn];
update_formula_label(frm, row.data_source);
update_advanced_formula_property(frm, cdt, cdn);
set_up_filters_editor(frm, cdt, cdn);
update_formula_description(frm, row.data_source);
},
calculation_formula(frm, cdt, cdn) {
update_advanced_formula_property(frm, cdt, cdn);
},
advanced_filtering(frm, cdt, cdn) {
set_up_filters_editor(frm, cdt, cdn);
},
});
// FILTERS EDITOR
function set_up_filters_editor(frm, cdt, cdn) {
const row = locals[cdt][cdn];
if (row.data_source !== "Account Data" || row.advanced_filtering) return;
const grid_row = frm.fields_dict["rows"].grid.get_row(cdn);
const wrapper = grid_row.get_field("filters_editor").$wrapper;
wrapper.empty();
const ACCOUNT = "Account";
const FIELD_IDX = 1;
const OPERATOR_IDX = 2;
const VALUE_IDX = 3;
// Parse saved filters
let saved_filters = [];
if (row.calculation_formula) {
try {
const parsed = JSON.parse(row.calculation_formula);
if (Array.isArray(parsed)) saved_filters = [parsed];
else if (parsed.and) saved_filters = parsed.and;
} catch (e) {
frappe.show_alert({
message: __("Invalid filter formula. Please check the syntax."),
indicator: "red",
});
}
}
if (saved_filters.length)
// Ensure every filter starts with "Account"
saved_filters = saved_filters.map((f) => [ACCOUNT, ...f]);
frappe.model.with_doctype(ACCOUNT, () => {
const filter_group = new frappe.ui.FilterGroup({
parent: wrapper,
doctype: ACCOUNT,
on_change: () => {
// only need [[field, operator, value]]
const filters = filter_group
.get_filters()
.map((f) => [f[FIELD_IDX], f[OPERATOR_IDX], f[VALUE_IDX]]);
const current = filters.length > 1 ? { and: filters } : filters[0];
frappe.model.set_value(cdt, cdn, "calculation_formula", JSON.stringify(current));
},
});
filter_group.add_filters_to_filter_group(saved_filters);
});
}
function update_advanced_formula_property(frm, cdt, cdn) {
const row = locals[cdt][cdn];
const is_advanced = is_advanced_formula(row);
frm.set_df_property("rows", "read_only", is_advanced, frm.doc.name, "advanced_filtering", cdn);
if (is_advanced && !row.advanced_filtering) {
row.advanced_filtering = 1;
frm.refresh_field("rows");
}
}
function is_advanced_formula(row) {
if (!row || row.data_source !== "Account Data") return false;
let parsed = null;
if (row.calculation_formula) {
try {
parsed = JSON.parse(row.calculation_formula);
} catch (e) {
console.warn("Invalid JSON in calculation_formula:", e);
return false;
}
}
if (Array.isArray(parsed)) return false;
if (parsed?.or) return true;
if (parsed?.and) return parsed.and.some((cond) => !Array.isArray(cond));
return false;
}
// ACCOUNTS TREE VIEW
function show_accounts_tree(template_rows, has_selection) {
// filtered rows
const account_rows = template_rows.filter((row) => row.data_source === "Account Data");
if (account_rows.length === 0) {
frappe.show_alert(__("No <strong>Account Data</strong> row found"));
return;
}
const dialog = new frappe.ui.Dialog({
title: __("Accounts Missing from Report"),
fields: [
{
fieldname: "company",
fieldtype: "Link",
options: "Company",
label: "Company",
reqd: 1,
default: frappe.defaults.get_user_default("Company"),
onchange: () => {
const company_field = dialog.get_field("company");
if (!company_field.value || company_field.value === company_field.last_value) return;
refresh_tree_view(dialog, account_rows);
},
},
{
fieldname: "view_type",
fieldtype: "Select",
options: ["Missing Accounts", "Filtered Accounts"],
label: "View",
default: has_selection ? "Filtered Accounts" : "Missing Accounts",
reqd: 1,
onchange: () => {
dialog.set_title(
dialog.get_value("view_type") === "Missing Accounts"
? __("Accounts Missing from Report")
: __("Accounts Included in Report")
);
refresh_tree_view(dialog, account_rows);
},
},
{
fieldname: "tip",
fieldtype: "HTML",
label: "Tip",
options: `
<div class="alert alert-success" role="alert">
Tip: Select report lines to view their accounts
</div>
`,
depends_on: has_selection ? "eval: false" : "eval: true",
},
{
fieldname: "tree_area",
fieldtype: "HTML",
label: "Chart of Accounts",
read_only: 1,
depends_on: "eval: doc.company",
},
],
primary_action_label: __("Done"),
primary_action() {
dialog.hide();
},
});
dialog.show();
refresh_tree_view(dialog, account_rows);
}
async function refresh_tree_view(dialog, account_rows) {
const missed = dialog.get_value("view_type") === "Missing Accounts";
const company = dialog.get_value("company");
const wrapper = dialog.get_field("tree_area").$wrapper;
wrapper.empty();
// get filtered accounts
const { message: filtered_accounts } = await frappe.call({
method: "erpnext.accounts.doctype.financial_report_template.financial_report_engine.get_filtered_accounts",
args: { company: company, account_rows: account_rows },
});
// render tree
const tree = new FilteredTree({
parent: wrapper,
label: company,
root_value: company,
method: "erpnext.accounts.doctype.financial_report_template.financial_report_engine.get_children_accounts",
args: { doctype: "Account", company: company, filtered_accounts: filtered_accounts, missed: missed },
toolbar: [],
});
tree.load_children(tree.root_node, true);
}
class FilteredTree extends frappe.ui.Tree {
render_children_of_all_nodes(data_list) {
data_list = this.get_filtered_data_list(data_list);
super.render_children_of_all_nodes(data_list);
}
get_filtered_data_list(data_list) {
let removed_nodes = new Set();
// Filter nodes with no data
data_list = data_list.filter((d) => {
if (d.data.length === 0) {
removed_nodes.add(d.parent);
return false;
}
return true;
});
// Remove references to removed nodes and iteratively remove empty parents
while (removed_nodes.size > 0) {
const current_removed = [...removed_nodes];
removed_nodes.clear();
data_list = data_list.filter((d) => {
d.data = d.data.filter((a) => !current_removed.includes(a.value));
if (d.data.length === 0) {
removed_nodes.add(d.parent);
return false;
}
return true;
});
}
return data_list;
}
}
function update_formula_label(frm, data_source) {
const grid = frm.fields_dict.rows.grid;
const field = grid.fields_map.calculation_formula;
if (!field) return;
const labels = {
"Account Data": "Account Filter",
"Custom API": "API Method Path",
};
grid.update_docfield_property(
"calculation_formula",
"label",
labels[data_source] || "Calculation Formula"
);
}
// FORMULA DESCRIPTION
function update_formula_description(frm, data_source) {
if (!data_source) return;
let grid = frm.fields_dict.rows.grid;
let field = grid.fields_map.formula_description;
if (!field) return;
// Common CSS styles and elements
const container_style = `style="padding: var(--padding-md); border: 1px solid var(--border-color); border-radius: var(--border-radius); margin-top: var(--margin-sm);"`;
const title_style = `style="margin-top: 0; color: var(--text-color);"`;
const subtitle_style = `style="color: var(--text-color); margin-bottom: var(--margin-xs);"`;
const text_style = `style="margin-bottom: var(--margin-sm); color: var(--text-muted);"`;
const list_style = `style="margin-bottom: var(--margin-sm); color: var(--text-muted); font-size: 0.9em;"`;
const note_style = `style="margin-bottom: 0; color: var(--text-muted); font-size: 0.9em;"`;
const tip_style = `style="margin-bottom: 0; color: var(--text-color); font-size: 0.85em;"`;
let description_html = "";
if (data_source === "Account Data") {
description_html = `
<div ${container_style}>
<h5 ${title_style}>Account Filter Guide</h5>
<p ${text_style}>Specify which accounts to include in this line.</p>
<h6 ${subtitle_style}>Basic Examples:</h6>
<ul ${list_style}>
<li><code>["account_type", "=", "Cash"]</code> - All Cash accounts</li>
<li><code>["root_type", "in", ["Asset", "Liability"]]</code> - All Asset and Liability accounts</li>
<li><code>["account_category", "like", "Revenue"]</code> - Revenue accounts</li>
</ul>
<h6 ${subtitle_style}>Multiple Conditions (AND/OR):</h6>
<ul ${list_style}>
<li><code>{"and": [["root_type", "=", "Asset"], ["account_type", "=", "Cash"]]}</code></li>
<li><code>{"or": [["account_category", "like", "Revenue"], ["account_category", "like", "Income"]]}</code></li>
</ul>
<p ${note_style}><strong>Available operators:</strong> <code>=, !=, in, not in, like, not like, is</code></p>
<p ${tip_style}><strong>Multi-Company Tip:</strong> Use fields like <code>account_type</code>, <code>root_type</code>, and <code>account_category</code> for templates that work across multiple companies.</p>
</div>`;
} else if (data_source === "Calculated Amount") {
description_html = `
<div ${container_style}>
<h5 ${title_style}>Formula Guide</h5>
<p ${text_style}>Create calculations using reference codes from other lines.</p>
<h6 ${subtitle_style}>Basic Examples:</h6>
<ul ${list_style}>
<li><code>REV100 + REV200</code> - Add two revenue lines</li>
<li><code>ASSETS - LIABILITIES</code> - Calculate equity</li>
<li><code>REVENUE * 0.1</code> - 10% of revenue</li>
</ul>
<h6 ${subtitle_style}>Common Functions:</h6>
<ul ${list_style}>
<li><code>abs(value)</code> - Remove negative sign</li>
<li><code>round(value)</code> - Round to whole number</li>
<li><code>max(val1, val2)</code> - Larger of two values</li>
<li><code>min(val1, val2)</code> - Smaller of two values</li>
</ul>
<p ${note_style}><strong>Required:</strong> Use "Reference Code" from other rows in your formulas.</p>
</div>`;
} else if (data_source === "Custom API") {
description_html = `
<div ${container_style}>
<h5 ${title_style}>Custom API Setup</h5>
<p ${text_style}>Path to your custom method that returns financial data.</p>
<h6 ${subtitle_style}>Format:</h6>
<ul ${list_style}>
<li><code>erpnext.custom.financial_apis.get_custom_revenue</code></li>
<li><code>my_app.financial_reports.get_kpi_data</code></li>
</ul>
<h6 ${subtitle_style}>Return Format:</h6>
<p ${text_style}>Numbers for each period: <code>[1000.0, 1200.0, 1150.0]</code></p>
</div>`;
} else if (data_source === "Blank Line") {
description_html = `
<div ${container_style}>
<h5 ${title_style}>Blank Line</h5>
<p ${text_style}>Adds empty space for better visual separation.</p>
<h6 ${subtitle_style}>Use For:</h6>
<ul ${list_style}>
<li>Separating major sections</li>
<li>Adding space before totals</li>
</ul>
<p ${note_style}><strong>Note:</strong> No formula needed - creates visual spacing only.</p>
</div>`;
} else if (data_source === "Column Break") {
description_html = `
<div ${container_style}>
<h5 ${title_style}>Column Break</h5>
<p ${text_style}>Creates a visual break for side-by-side layout.</p>
<h6 ${subtitle_style}>Use For:</h6>
<ul ${list_style}>
<li>Horizontal P&L statements</li>
<li>Side-by-side Balance Sheet sections</li>
</ul>
<p ${note_style}><strong>Note:</strong> No formula needed - this is for formatting only.</p>
</div>`;
} else if (data_source === "Section Break") {
description_html = `
<div ${container_style}>
<h5 ${title_style}>Section Break</h5>
<p ${text_style}>Creates a visual break for separating different sections.</p>
<h6 ${subtitle_style}>Use For:</h6>
<ul ${list_style}>
<li>Separating major sections in a report - say trading & profit and loss</li>
<li>Improving readability by adding space</li>
</ul>
<p ${note_style}><strong>Note:</strong> No formula needed - this is for formatting only.</p>
</div>`;
}
grid.update_docfield_property("formula_description", "options", description_html);
}

View File

@@ -1,102 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:template_name",
"creation": "2025-08-02 04:44:15.184541",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"template_name",
"report_type",
"module",
"column_break_lvnq",
"disabled",
"section_break_fvlw",
"rows"
],
"fields": [
{
"description": "Descriptive name for your template (e.g., 'Standard P&L', 'Detailed Balance Sheet')",
"fieldname": "template_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Template Name",
"reqd": 1,
"unique": 1
},
{
"description": "Type of financial statement this template generates",
"fieldname": "report_type",
"fieldtype": "Select",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Report Type",
"options": "\nProfit and Loss Statement\nBalance Sheet\nCash Flow\nCustom Financial Statement"
},
{
"depends_on": "eval:frappe.boot.developer_mode",
"fieldname": "module",
"fieldtype": "Link",
"label": "Module (for Export)",
"options": "Module Def"
},
{
"fieldname": "column_break_lvnq",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_fvlw",
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 1,
"fieldname": "rows",
"fieldtype": "Table",
"label": "Report Line Items",
"options": "Financial Report Row"
},
{
"default": "0",
"description": "Disable template to prevent use in reports",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-11-14 00:11:03.508139",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Financial Report Template",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"write": 1
},
{
"read": 1,
"role": "Accounts User"
},
{
"read": 1,
"role": "Auditor"
}
],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"title_field": "template_name"
}

View File

@@ -1,179 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import os
import shutil
import frappe
from frappe import _
from frappe.model.document import Document
from erpnext.accounts.doctype.account_category.account_category import import_account_categories
from erpnext.accounts.doctype.financial_report_template.financial_report_validation import TemplateValidator
class FinancialReportTemplate(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.financial_report_row.financial_report_row import FinancialReportRow
disabled: DF.Check
module: DF.Link | None
report_type: DF.Literal[
"", "Profit and Loss Statement", "Balance Sheet", "Cash Flow", "Custom Financial Statement"
]
rows: DF.Table[FinancialReportRow]
template_name: DF.Data
# end: auto-generated types
def validate(self):
validator = TemplateValidator(self)
result = validator.validate()
result.notify_user()
def on_update(self):
self._export_template()
def on_trash(self):
self._delete_template()
def _export_template(self):
from frappe.modules.utils import export_module_json
if not self.module:
return
export_module_json(self, True, self.module)
self._export_account_categories()
def _delete_template(self):
if not self.module or not frappe.conf.developer_mode:
return
module_path = frappe.get_module_path(self.module)
dir_path = os.path.join(module_path, "financial_report_template", frappe.scrub(self.name))
shutil.rmtree(dir_path, ignore_errors=True)
def _export_account_categories(self):
import json
from erpnext.accounts.doctype.financial_report_template.financial_report_engine import (
FormulaFieldExtractor,
)
if not self.module or not frappe.conf.developer_mode or frappe.flags.in_import:
return
# Extract category from rows
extractor = FormulaFieldExtractor(
field_name="account_category", exclude_operators=["like", "not like"]
)
account_data_rows = [row for row in self.rows if row.data_source == "Account Data"]
category_names = extractor.extract_from_rows(account_data_rows)
if not category_names:
return
# Get path
module_path = frappe.get_module_path(self.module)
categories_file = os.path.join(module_path, "financial_report_template", "account_categories.json")
# Load existing categories
existing_categories = {}
if os.path.exists(categories_file):
try:
with open(categories_file) as f:
existing_data = json.load(f)
existing_categories = {cat["account_category_name"]: cat for cat in existing_data}
except (json.JSONDecodeError, KeyError):
pass # Create new file
# Fetch categories from database
if category_names:
db_categories = frappe.get_all(
"Account Category",
filters={"account_category_name": ["in", list(category_names)]},
fields=["account_category_name", "description"],
)
for cat in db_categories:
existing_categories[cat["account_category_name"]] = cat
# Sort by category name
sorted_categories = sorted(existing_categories.values(), key=lambda x: x["account_category_name"])
# Write to file
os.makedirs(os.path.dirname(categories_file), exist_ok=True)
with open(categories_file, "w") as f:
json.dump(sorted_categories, f, indent=2)
def sync_financial_report_templates(chart_of_accounts=None, existing_company=None):
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import get_chart
# If COA is being created for an existing company,
# skip syncing templates as they are likely already present
if existing_company:
return
# Allow regional templates to completely override ERPNext
# templates based on the chart of accounts selected
disable_default_financial_report_template = False
if chart_of_accounts:
coa = get_chart(chart_of_accounts)
if coa.get("disable_default_financial_report_template", False):
disable_default_financial_report_template = True
installed_apps = frappe.get_installed_apps()
for app in installed_apps:
if disable_default_financial_report_template and app == "erpnext":
continue
_sync_templates_for(app)
def _sync_templates_for(app_name):
templates = []
for module_name in frappe.local.app_modules.get(app_name) or []:
module_path = frappe.get_module_path(module_name)
template_path = os.path.join(module_path, "financial_report_template")
if not os.path.isdir(template_path):
continue
import_account_categories(template_path)
for template_dir in os.listdir(template_path):
json_file = os.path.join(template_path, template_dir, f"{template_dir}.json")
if os.path.isfile(json_file):
templates.append(json_file)
if not templates:
return
# ensure files are not exported
frappe.flags.in_import = True
for template_path in templates:
with open(template_path) as f:
template_data = frappe._dict(frappe.parse_json(f.read()))
template_name = template_data.get("name")
if not frappe.db.exists("Financial Report Template", template_name):
doc = frappe.get_doc(template_data)
doc.flags.ignore_mandatory = True
doc.flags.ignore_permissions = True
doc.flags.ignore_validate = True
doc.insert()
frappe.flags.in_import = False

View File

@@ -1,545 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import ast
import json
import re
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, ClassVar
import frappe
from frappe import _
from frappe.database.operator_map import OPERATOR_MAP
from frappe.database.query import SQLFunctionParser
@dataclass
class ValidationIssue:
"""Represents a single validation issue"""
message: str
row_idx: int | None = None
field: str | None = None
details: dict[str, Any] = None
def __post_init__(self):
if self.details is None:
self.details = {}
def __str__(self) -> str:
prefix = f"Row {self.row_idx}: " if self.row_idx else ""
field_info = f"[{self.field}] " if self.field else ""
message = f"{prefix}{field_info}{self.message}"
return _(message)
@dataclass
class ValidationResult:
issues: list[ValidationIssue] = field(default_factory=list)
warnings: list[ValidationIssue] = field(default_factory=list)
@property
def is_valid(self) -> bool:
return len(self.issues) == 0
@property
def has_warnings(self) -> bool:
return len(self.warnings) > 0
@property
def error_count(self) -> int:
return len(self.issues)
@property
def warning_count(self) -> int:
return len(self.warnings)
def merge(self, other: "ValidationResult") -> "ValidationResult":
self.issues.extend(other.issues)
self.warnings.extend(other.warnings)
return self
def add_error(self, issue: ValidationIssue) -> None:
"""Add a critical error that prevents functionality"""
self.issues.append(issue)
def add_warning(self, issue: ValidationIssue) -> None:
"""Add a warning for recommendatory validation"""
self.warnings.append(issue)
def notify_user(self) -> None:
warnings = "<br><br>".join(str(w) for w in self.warnings)
errors = "<br><br>".join(str(e) for e in self.issues)
if warnings:
frappe.msgprint(warnings, title=_("Warnings"), indicator="orange")
if errors:
frappe.throw(errors, title=_("Errors"))
class TemplateValidator:
"""Main validator that orchestrates all validations"""
def __init__(self, template):
self.template = template
self.validators = [
TemplateStructureValidator(),
DependencyValidator(template),
]
self.formula_validator = FormulaValidator(template)
def validate(self) -> ValidationResult:
result = ValidationResult([])
# Run template-level validators
for validator in self.validators:
result.merge(validator.validate(self.template))
# Run row-level validations
account_fields = {field.fieldname for field in frappe.get_meta("Account").fields}
for row in self.template.rows:
result.merge(self.formula_validator.validate(row, account_fields))
return result
class Validator(ABC):
@abstractmethod
def validate(self, context: Any) -> ValidationResult:
pass
class TemplateStructureValidator(Validator):
def validate(self, template) -> ValidationResult:
result = ValidationResult()
result.merge(self._validate_reference_codes(template))
result.merge(self._validate_required_fields(template))
return result
def _validate_reference_codes(self, template) -> ValidationResult:
result = ValidationResult()
used_codes = set()
for row in template.rows:
if not row.reference_code:
continue
ref_code = row.reference_code.strip()
# Check format
if not re.match(r"^[A-Za-z][A-Za-z0-9_-]*$", ref_code):
result.add_error(
ValidationIssue(
message=f"Invalid line reference format: '{ref_code}'. Must start with letter and contain only letters, numbers, underscores, and hyphens",
row_idx=row.idx,
)
)
# Check uniqueness
if ref_code in used_codes:
result.add_error(
ValidationIssue(
message=f"Duplicate line reference: '{ref_code}'",
row_idx=row.idx,
)
)
used_codes.add(ref_code)
return result
def _validate_required_fields(self, template) -> ValidationResult:
result = ValidationResult()
for row in template.rows:
# Balance type required
if row.data_source == "Account Data" and not row.balance_type:
result.add_error(
ValidationIssue(
message="Balance Type is required for Account Data",
row_idx=row.idx,
)
)
# Calculation formula required
if row.data_source in ["Account Data", "Calculated Amount", "Custom API"]:
if not row.calculation_formula:
result.add_error(
ValidationIssue(
message=f"Formula is required for {row.data_source}",
row_idx=row.idx,
)
)
return result
class DependencyValidator(Validator):
def __init__(self, template):
self.template = template
self.dependencies = self._build_dependency_graph()
def validate(self, context=None) -> ValidationResult:
result = ValidationResult()
result.merge(self._validate_circular_dependencies())
result.merge(self._validate_missing_dependencies())
return result
def _build_dependency_graph(self) -> dict[str, list[str]]:
graph = {}
available_codes = {row.reference_code for row in self.template.rows if row.reference_code}
for row in self.template.rows:
if row.reference_code and row.data_source == "Calculated Amount" and row.calculation_formula:
deps = extract_reference_codes_from_formula(row.calculation_formula, list(available_codes))
if deps:
graph[row.reference_code] = deps
return graph
def _validate_circular_dependencies(self) -> ValidationResult:
"""
Efficient cycle detection using DFS (Depth-First Search) with three-color algorithm:
- WHITE (0): unvisited node
- GRAY (1): currently being processed (on recursion stack)
- BLACK (2): fully processed
Example cycle detection:
A → B → C → A (cycle detected when A is GRAY and visited again)
"""
result = ValidationResult()
WHITE, GRAY, BLACK = 0, 1, 2
colors = {node: WHITE for node in self.dependencies}
def dfs(node, path):
if node not in colors:
return # External dependency
if colors[node] == GRAY:
# Found cycle
cycle_start = path.index(node)
cycle = [*path[cycle_start:], node]
result.add_error(
ValidationIssue(
message=f"Circular dependency detected: {''.join(cycle)}",
)
)
return
if colors[node] == BLACK:
return # Already processed
colors[node] = GRAY
path.append(node)
for neighbor in self.dependencies.get(node, []):
dfs(neighbor, path.copy())
colors[node] = BLACK
for node in self.dependencies:
if colors[node] == WHITE:
dfs(node, [])
return result
def _validate_missing_dependencies(self) -> ValidationResult:
available = {row.reference_code for row in self.template.rows if row.reference_code}
result = ValidationResult()
for ref_code, deps in self.dependencies.items():
undefined = [d for d in deps if d not in available]
if undefined:
row_idx = self._get_row_idx(ref_code)
result.add_error(
ValidationIssue(
message=f"Line References undefined in Formula: {', '.join(undefined)}",
row_idx=row_idx,
)
)
return result
def _get_row_idx(self, reference_code: str) -> int | None:
for row in self.template.rows:
if row.reference_code == reference_code:
return row.idx
return None
class CalculationFormulaValidator(Validator):
"""Validates calculation formulas used in Calculated Amount rows"""
def __init__(self, reference_codes: set[str]):
self.reference_codes = reference_codes
def validate(self, row) -> ValidationResult:
"""Validate calculation formula for a single row"""
result = ValidationResult()
if row.data_source != "Calculated Amount":
return result
if not row.calculation_formula:
result.add_error(
ValidationIssue(
message="Formula is required for Calculated Amount",
row_idx=row.idx,
field="Formula",
)
)
return result
formula = self._preprocess_formula(row.calculation_formula)
row.calculation_formula = formula
# Check parentheses
if not self._are_parentheses_balanced(formula):
result.add_error(
ValidationIssue(
message="Formula has unbalanced parentheses",
row_idx=row.idx,
)
)
return result
# Check self-reference
available_codes = list(self.reference_codes)
refs = extract_reference_codes_from_formula(formula, available_codes)
if row.reference_code and row.reference_code in refs:
result.add_error(
ValidationIssue(
message=f"Formula references itself ('{row.reference_code}')",
row_idx=row.idx,
)
)
# Check undefined references
undefined = set(refs) - set(available_codes)
if undefined:
result.add_error(
ValidationIssue(
message=f"Formula references undefined codes: {', '.join(undefined)}",
row_idx=row.idx,
)
)
# Try to evaluate with dummy values
eval_error = self._test_formula_evaluation(formula, available_codes)
if eval_error:
result.add_error(
ValidationIssue(
message=f"Formula evaluation error: {eval_error}",
row_idx=row.idx,
)
)
return result
def _preprocess_formula(self, formula: str) -> str:
if not formula or not isinstance(formula, str):
return ""
return formula.strip()
@staticmethod
def _are_parentheses_balanced(formula: str) -> bool:
return formula.count("(") == formula.count(")")
def _test_formula_evaluation(self, formula: str, available_codes: list[str]) -> str | None:
try:
context = {code: 1.0 for code in available_codes}
context.update(
{
"abs": abs,
"round": round,
"min": min,
"max": max,
"sum": sum,
"sqrt": lambda x: x**0.5,
"pow": pow,
"ceil": lambda x: int(x) + (1 if x % 1 else 0),
"floor": lambda x: int(x),
}
)
result = frappe.safe_eval(formula, eval_globals=None, eval_locals=context)
if not isinstance(result, (int, float)): # noqa: UP038
return f"Formula must return a numeric value, got {type(result).__name__}"
return None
except Exception as e:
return str(e)
class AccountFilterValidator(Validator):
"""Validates account filter expressions used in Account Data rows"""
def __init__(self, account_fields: set | None = None):
self.account_fields = account_fields or set(frappe.get_meta("Account")._valid_columns)
def validate(self, row) -> ValidationResult:
result = ValidationResult()
if row.data_source != "Account Data":
return result
if not row.calculation_formula:
result.add_error(
ValidationIssue(
message="Account filter is required for Account Data",
row_idx=row.idx,
field="Formula",
)
)
return result
try:
filter_config = json.loads(row.calculation_formula)
error = self._validate_filter_structure(filter_config, self.account_fields)
if error:
result.add_error(
ValidationIssue(
message=error,
row_idx=row.idx,
field="Account Filter",
)
)
except json.JSONDecodeError as e:
result.add_error(
ValidationIssue(
message=f"Invalid JSON format: {e!s}",
row_idx=row.idx,
field="Account Filter",
)
)
return result
def _validate_filter_structure(self, filter_config, account_fields: set) -> str | None:
# simple condition: [field, operator, value]
if isinstance(filter_config, list):
if len(filter_config) != 3:
return "Filter must be [field, operator, value]"
field, operator, value = filter_config
if not isinstance(field, str) or not isinstance(operator, str):
return "Field and operator must be strings"
if field not in account_fields:
return f"Field '{field}' is not a valid account field"
if operator.casefold() not in OPERATOR_MAP:
return f"Invalid operator '{operator}'"
if operator in ["in", "not in"] and not isinstance(value, list):
return f"Operator '{operator}' requires a list value"
# logical condition: {"and": [condition1, condition2]}
elif isinstance(filter_config, dict):
if len(filter_config) != 1:
return "Logical condition must have exactly one operator"
op = next(iter(filter_config.keys())).lower()
if op not in ["and", "or"]:
return "Logical operators must be 'and' or 'or'"
conditions = filter_config[next(iter(filter_config.keys()))]
if not isinstance(conditions, list) or len(conditions) < 1:
return "Logical conditions need at least 1 sub-condition"
# recursive
for condition in conditions:
error = self._validate_filter_structure(condition, account_fields)
if error:
return error
else:
return "Filter must be a list or dict"
return None
class FormulaValidator(Validator):
def __init__(self, template):
self.template = template
reference_codes = {row.reference_code for row in template.rows if row.reference_code}
self.calculation_validator = CalculationFormulaValidator(reference_codes)
self.account_filter_validator = AccountFilterValidator()
def validate(self, row, account_fields: set) -> ValidationResult:
result = ValidationResult()
if not row.calculation_formula:
return result
if row.data_source == "Calculated Amount":
return self.calculation_validator.validate(row)
elif row.data_source == "Account Data":
# Update account fields if provided
if account_fields:
self.account_filter_validator.account_fields = account_fields
return self.account_filter_validator.validate(row)
elif row.data_source == "Custom API":
result.merge(self._validate_custom_api(row))
return result
def _validate_custom_api(self, row) -> ValidationResult:
result = ValidationResult()
api_path = row.calculation_formula
if "." not in api_path:
result.add_error(
ValidationIssue(
message="Custom API path should be in format: app.module.method",
row_idx=row.idx,
field="Formula",
)
)
return result
# Method exists?
try:
module_path, method_name = api_path.rsplit(".", 1)
module = frappe.get_module(module_path)
if not hasattr(module, method_name):
result.add_error(
ValidationIssue(
message=f"Method '{method_name}' not found in module '{module_path}' (might be environment-specific)",
row_idx=row.idx,
field="Formula",
)
)
except Exception as e:
result.add_error(
ValidationIssue(
message=f"Could not validate API path: {e!s}",
row_idx=row.idx,
field="Formula",
)
)
return result
def extract_reference_codes_from_formula(formula: str, available_codes: list[str]) -> list[str]:
found_codes = []
for code in available_codes:
# Match complete words only to avoid partial matches
pattern = r"\b" + re.escape(code) + r"\b"
if re.search(pattern, formula):
found_codes.append(code)
return found_codes

View File

@@ -1,79 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.tests import IntegrationTestCase
from frappe.tests.utils import make_test_records
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class TestFinancialReportTemplate(IntegrationTestCase):
pass
class FinancialReportTemplateTestCase(IntegrationTestCase):
"""Utility class with common setup and helper methods for all test classes"""
@classmethod
def setUpClass(cls):
"""Set up test data"""
make_test_records("Company")
make_test_records("Fiscal Year")
cls.create_test_template()
@classmethod
def create_test_template(cls):
"""Create a test financial report template"""
if not frappe.db.exists("Financial Report Template", "Test P&L Template"):
template = frappe.get_doc(
{
"doctype": "Financial Report Template",
"template_name": "Test P&L Template",
"report_type": "Profit and Loss Statement",
"rows": [
{
"reference_code": "INC001",
"display_name": "Income",
"indentation_level": 0,
"data_source": "Account Data",
"balance_type": "Closing Balance",
"bold_text": 1,
"calculation_formula": '["root_type", "=", "Income"]',
},
{
"reference_code": "EXP001",
"display_name": "Expenses",
"indentation_level": 0,
"data_source": "Account Data",
"balance_type": "Closing Balance",
"bold_text": 1,
"calculation_formula": '["root_type", "=", "Expense"]',
},
{
"reference_code": "NET001",
"display_name": "Net Profit/Loss",
"indentation_level": 0,
"data_source": "Calculated Amount",
"bold_text": 1,
"calculation_formula": "INC001 - EXP001",
},
],
}
)
template.insert()
cls.test_template = frappe.get_doc("Financial Report Template", "Test P&L Template")
@staticmethod
def create_test_template_with_rows(rows_data):
"""Helper method to create test template with specific rows"""
template_name = f"Test Template {frappe.generate_hash()[:8]}"
template = frappe.get_doc(
{"doctype": "Financial Report Template", "template_name": template_name, "rows": rows_data}
)
return template

View File

@@ -99,7 +99,7 @@ class FiscalYear(Document):
)
overlap = False
if not self.get("companies") and not company_for_existing:
if not self.get("companies") or not company_for_existing:
overlap = True
for d in self.get("companies"):

View File

@@ -25,27 +25,6 @@ class TestFiscalYear(IntegrationTestCase):
self.assertRaises(frappe.exceptions.InvalidDates, fy.insert)
def test_company_fiscal_year_overlap(self):
for name in ["_Test Global FY 2001", "_Test Company FY 2001"]:
if frappe.db.exists("Fiscal Year", name):
frappe.delete_doc("Fiscal Year", name)
global_fy = frappe.new_doc("Fiscal Year")
global_fy.year = "_Test Global FY 2001"
global_fy.year_start_date = "2001-04-01"
global_fy.year_end_date = "2002-03-31"
global_fy.insert()
company_fy = frappe.new_doc("Fiscal Year")
company_fy.year = "_Test Company FY 2001"
company_fy.year_start_date = "2001-01-01"
company_fy.year_end_date = "2001-12-31"
company_fy.append("companies", {"company": "_Test Company"})
company_fy.insert()
self.assertTrue(frappe.db.exists("Fiscal Year", global_fy.name))
self.assertTrue(frappe.db.exists("Fiscal Year", company_fy.name))
def test_record_generator():
test_records = [

View File

@@ -1,63 +0,0 @@
{
"actions": [],
"creation": "2025-07-17 12:24:05.609186",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"item_row",
"tax_row",
"rate",
"amount",
"taxable_amount"
],
"fields": [
{
"fieldname": "item_row",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Item Row",
"reqd": 1
},
{
"fieldname": "tax_row",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Tax Row",
"reqd": 1
},
{
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Tax Rate"
},
{
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Tax Amount",
"options": "Company:company:default_currency"
},
{
"fieldname": "taxable_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Taxable Amount",
"options": "Company:company:default_currency"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-09-26 15:54:19.750714",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Wise Tax Detail",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,27 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class ItemWiseTaxDetail(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
amount: DF.Currency
item_row: DF.Data
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
rate: DF.Float
tax_row: DF.Data
taxable_amount: DF.Currency
# end: auto-generated types
pass

View File

@@ -111,10 +111,6 @@ frappe.ui.form.on("Journal Entry", {
}
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
$.each(frm.doc.accounts || [], function (i, row) {
erpnext.journal_entry.set_exchange_rate(frm, row.doctype, row.name);
});
},
before_save: function (frm) {
if (frm.doc.docstatus == 0 && !frm.doc.is_system_generated) {

View File

@@ -1774,7 +1774,7 @@ def get_exchange_rate(
# The date used to retreive the exchange rate here is the date passed
# in as an argument to this function.
elif (not flt(exchange_rate) or flt(exchange_rate) == 1) and account_currency and posting_date:
elif (not exchange_rate or flt(exchange_rate) == 1) and account_currency and posting_date:
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
else:
exchange_rate = 1

View File

@@ -106,6 +106,7 @@
"fieldname": "account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"no_copy": 1,
"options": "Currency",
"print_hide": 1,
"read_only": 1
@@ -270,8 +271,7 @@
"label": "Advance Voucher Type",
"no_copy": 1,
"options": "DocType",
"read_only": 1,
"search_index": 1
"read_only": 1
},
{
"fieldname": "advance_voucher_no",
@@ -279,14 +279,13 @@
"label": "Advance Voucher No",
"no_copy": 1,
"options": "advance_voucher_type",
"read_only": 1,
"search_index": 1
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-10-27 13:48:32.805100",
"modified": "2025-09-29 13:01:48.916517",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@@ -577,8 +577,6 @@ frappe.ui.form.on("Payment Entry", {
paid_from: function (frm) {
if (frm.set_party_account_based_on_party) return;
frm.events.set_company_bank_account(frm);
frm.events.set_account_currency_and_balance(
frm,
frm.doc.paid_from,
@@ -595,8 +593,6 @@ frappe.ui.form.on("Payment Entry", {
paid_to: function (frm) {
if (frm.set_party_account_based_on_party) return;
frm.events.set_company_bank_account(frm);
frm.events.set_account_currency_and_balance(
frm,
frm.doc.paid_to,
@@ -1329,8 +1325,6 @@ frappe.ui.form.on("Payment Entry", {
},
bank_account: function (frm) {
if (frm.set_company_bank_account_based_on_coa) return;
const field = frm.doc.payment_type == "Pay" ? "paid_from" : "paid_to";
if (frm.doc.bank_account && ["Pay", "Receive"].includes(frm.doc.payment_type)) {
frappe.call({
@@ -1369,34 +1363,6 @@ frappe.ui.form.on("Payment Entry", {
}
},
set_company_bank_account: function (frm) {
if (!["Pay", "Receive"].includes(frm.doc.payment_type)) return;
const field = frm.doc.payment_type == "Pay" ? "paid_from" : "paid_to";
if (!frm.doc.company || !frm.doc[field]) return;
frm.set_company_bank_account_based_on_coa = true;
frappe.call({
method: "frappe.client.get_value",
args: {
doctype: "Bank Account",
filters: {
company: frm.doc.company,
account: frm.doc[field],
disabled: 0,
},
fieldname: ["name"],
},
callback: async function (r) {
if (r.message) await frm.set_value("bank_account", r.message.name);
frm.set_company_bank_account_based_on_coa = false;
},
});
},
sales_taxes_and_charges_template: function (frm) {
frm.trigger("fetch_taxes_from_template");
},
@@ -1455,6 +1421,7 @@ frappe.ui.form.on("Payment Entry", {
$.each(frm.doc["taxes"] || [], function (i, tax) {
frm.events.validate_taxes_and_charges(tax);
frm.events.validate_inclusive_tax(tax);
tax.item_wise_tax_detail = {};
let tax_fields = [
"total",
"tax_fraction_for_current_item",

View File

@@ -1437,7 +1437,6 @@ class PaymentEntry(AccountsController):
else allocated_amount_in_company_currency / self.transaction_exchange_rate,
"advance_voucher_type": d.advance_voucher_type,
"advance_voucher_no": d.advance_voucher_no,
"transaction_exchange_rate": self.target_exchange_rate,
},
item=self,
)

View File

@@ -61,22 +61,6 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
},
};
});
this.frm.set_query("cost_center", "payments", () => {
return {
filters: {
company: this.frm.doc.company,
is_group: 0,
},
};
});
this.frm.set_query("cost_center", "allocation", () => {
return {
filters: {
company: this.frm.doc.company,
is_group: 0,
},
};
});
}
refresh() {
@@ -401,16 +385,6 @@ frappe.ui.form.on("Payment Reconciliation Allocation", {
// filter payment
let payment = frm.doc.payments.filter((x) => x.reference_name == row.reference_name);
let amount = payment[0].amount;
for (const d of frm.doc.allocation) {
if (row.reference_name == d.reference_name && amount) {
if (d.allocated_amount <= amount) {
d.amount = amount;
amount -= d.allocated_amount;
}
}
}
frm.call({
doc: frm.doc,
method: "calculate_difference_on_allocation_change",

View File

@@ -72,7 +72,7 @@ class PaymentReconciliation(Document):
self.common_filter_conditions = []
self.accounting_dimension_filter_conditions = []
self.ple_posting_date_filter = []
self.dimensions = get_dimensions(with_cost_center_and_project=True)[0]
self.dimensions = get_dimensions()[0]
def load_from_db(self):
# 'modified' attribute is required for `run_doc_method` to work properly.

View File

@@ -4,8 +4,6 @@
frappe.ui.form.on("Period Closing Voucher", {
onload: function (frm) {
if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date());
frm.ignore_doctypes_on_cancel_all = ["Process Period Closing Voucher"];
},
setup: function (frm) {

View File

@@ -132,11 +132,7 @@ class PeriodClosingVoucher(AccountsController):
def on_submit(self):
self.db_set("gle_processing_status", "In Progress")
if frappe.get_single_value("Accounts Settings", "use_legacy_controller_for_pcv"):
self.make_gl_entries()
else:
ppcv = frappe.get_doc({"doctype": "Process Period Closing Voucher", "parent_pcv": self.name})
ppcv.save().submit()
self.make_gl_entries()
def on_cancel(self):
self.ignore_linked_doctypes = (
@@ -144,29 +140,11 @@ class PeriodClosingVoucher(AccountsController):
"Stock Ledger Entry",
"Payment Ledger Entry",
"Account Closing Balance",
"Process Period Closing Voucher",
)
self.block_if_future_closing_voucher_exists()
if not frappe.get_single_value("Accounts Settings", "use_legacy_controller_for_pcv"):
self.cancel_process_pcv_docs()
self.db_set("gle_processing_status", "In Progress")
self.cancel_gl_entries()
def cancel_process_pcv_docs(self):
ppcvs = frappe.db.get_all("Process Period Closing Voucher", {"parent_pcv": self.name, "docstatus": 1})
for x in ppcvs:
frappe.get_doc("Process Period Closing Voucher", x.name).cancel()
def on_trash(self):
super().on_trash()
ppcvs = frappe.db.get_all(
"Process Period Closing Voucher", {"parent_pcv": self.name, "docstatus": ["in", [1, 2]]}
)
for x in ppcvs:
frappe.delete_doc("Process Period Closing Voucher", x.name, force=True, ignore_permissions=True)
def make_gl_entries(self):
if frappe.db.estimate_count("GL Entry") > 100_000:
frappe.enqueue(
@@ -475,15 +453,8 @@ def process_gl_and_closing_entries(doc):
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
frappe.log_error(title=_("Period Closing Voucher {0} GL Entry Processing Failed").format(doc.name))
frappe.db.set_value(
doc.doctype,
doc.name,
{
"error_message": str(e),
"gle_processing_status": "Failed",
},
)
frappe.log_error(e)
frappe.db.set_value(doc.doctype, doc.name, "gle_processing_status", "Failed")
def process_cancellation(voucher_type, voucher_no):
@@ -495,17 +466,8 @@ def process_cancellation(voucher_type, voucher_no):
frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
frappe.log_error(
title=_("Period Closing Voucher {0} GL Entry Cancellation Failed").format(voucher_no)
)
frappe.db.set_value(
voucher_type,
voucher_no,
{
"error_message": str(e),
"gle_processing_status": "Failed",
},
)
frappe.log_error(e)
frappe.db.set_value("Period Closing Voucher", voucher_no, "gle_processing_status", "Failed")
def delete_closing_entries(voucher_no):

View File

@@ -13,10 +13,6 @@ from erpnext.accounts.utils import get_fiscal_year
class TestPeriodClosingVoucher(IntegrationTestCase):
def setUp(self):
super().setUp()
frappe.db.set_single_value("Accounts Settings", "use_legacy_controller_for_pcv", 1)
def test_closing_entry(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")

View File

@@ -70,7 +70,6 @@
"taxes",
"sec_tax_breakup",
"other_charges_calculation",
"item_wise_tax_details",
"section_break_43",
"base_total_taxes_and_charges",
"column_break_47",
@@ -1603,14 +1602,6 @@
"fieldtype": "Data",
"is_virtual": 1,
"label": "Last Scanned Warehouse"
},
{
"fieldname": "item_wise_tax_details",
"fieldtype": "Table",
"hidden": 1,
"label": "Item Wise Tax Details",
"no_copy": 1,
"options": "Item Wise Tax Detail"
}
],
"icon": "fa fa-file-text",

View File

@@ -31,7 +31,6 @@ class POSInvoice(SalesInvoice):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.item_wise_tax_detail.item_wise_tax_detail import ItemWiseTaxDetail
from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule
from erpnext.accounts.doctype.pos_invoice_item.pos_invoice_item import POSInvoiceItem
from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail
@@ -100,7 +99,6 @@ class POSInvoice(SalesInvoice):
is_opening: DF.Literal["No", "Yes"]
is_pos: DF.Check
is_return: DF.Check
item_wise_tax_details: DF.Table[ItemWiseTaxDetail]
items: DF.Table[POSInvoiceItem]
language: DF.Data | None
letter_head: DF.Link | None

View File

@@ -160,6 +160,7 @@
"oldfieldname": "description",
"oldfieldtype": "Text",
"print_width": "200px",
"reqd": 1,
"width": "200px"
},
{
@@ -857,15 +858,14 @@
],
"istable": 1,
"links": [],
"modified": "2025-11-12 18:11:11.818015",
"modified": "2024-05-07 15:56:53.343317",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Item",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -36,7 +36,7 @@ class POSInvoiceItem(SalesInvoiceItem):
delivered_by_supplier: DF.Check
delivered_qty: DF.Float
delivery_note: DF.Link | None
description: DF.TextEditor | None
description: DF.TextEditor
discount_amount: DF.Currency
discount_percentage: DF.Percent
distributed_discount_amount: DF.Currency

View File

@@ -17,6 +17,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
from erpnext.controllers.sales_and_purchase_return import get_sales_invoice_item_from_consolidated_invoice
from erpnext.controllers.taxes_and_totals import ItemWiseTaxDetail
class POSInvoiceMergeLog(Document):
@@ -155,6 +156,7 @@ class POSInvoiceMergeLog(Document):
sales_invoice.save()
sales_invoice.submit()
self.consolidated_invoice = sales_invoice.name
return sales_invoice
@@ -205,7 +207,7 @@ class POSInvoiceMergeLog(Document):
return return_invoices
def merge_pos_invoice_into(self, invoice, data):
items, payments, taxes, item_tax_details = [], [], [], []
items, payments, taxes = [], [], []
loyalty_amount_sum, loyalty_points_sum = 0, 0
@@ -215,8 +217,6 @@ class POSInvoiceMergeLog(Document):
loyalty_amount_sum, loyalty_points_sum, idx = 0, 0, 1
for doc in data:
old_new_item_map = frappe._dict()
old_new_tax_map = frappe._dict()
map_doc(doc, invoice, table_map={"doctype": invoice.doctype})
if doc.get("posting_date"):
@@ -244,7 +244,6 @@ class POSInvoiceMergeLog(Document):
if item.serial_and_batch_bundle:
si_item.serial_and_batch_bundle = item.serial_and_batch_bundle
items.append(si_item)
old_new_item_map[item.name] = si_item
for tax in doc.get("taxes"):
found = False
@@ -254,7 +253,7 @@ class POSInvoiceMergeLog(Document):
t.base_tax_amount = flt(t.base_tax_amount) + flt(
tax.base_tax_amount_after_discount_amount
)
old_new_tax_map[tax.name] = t
update_item_wise_tax_detail(t, tax)
found = True
if not found:
tax.charge_type = "Actual"
@@ -264,9 +263,8 @@ class POSInvoiceMergeLog(Document):
tax.included_in_print_rate = 0
tax.tax_amount = tax.tax_amount_after_discount_amount
tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
tax.dont_recompute_tax = 1
tax.item_wise_tax_detail = tax.item_wise_tax_detail
taxes.append(tax)
old_new_tax_map[tax.name] = tax
for payment in doc.get("payments"):
found = False
@@ -283,16 +281,6 @@ class POSInvoiceMergeLog(Document):
base_rounding_adjustment += doc.base_rounding_adjustment
base_rounded_total += doc.base_rounded_total
for d in doc.get("item_wise_tax_details"):
row = frappe._dict(
item=old_new_item_map[d.item_row],
tax=old_new_tax_map[d.tax_row],
amount=d.amount,
rate=d.rate,
taxable_amount=d.taxable_amount,
)
item_tax_details.append(row)
if loyalty_points_sum:
invoice.redeem_loyalty_points = 1
invoice.loyalty_points = loyalty_points_sum
@@ -354,7 +342,6 @@ class POSInvoiceMergeLog(Document):
invoice.set("sales_partner", None)
invoice.set("commission_rate", 0)
invoice.set("total_commission", 0)
invoice._item_wise_tax_details = item_tax_details
return invoice
@@ -432,6 +419,24 @@ class POSInvoiceMergeLog(Document):
si.cancel()
def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail)
tax_row_detail = json.loads(tax_row.item_wise_tax_detail)
if not consolidated_tax_detail:
consolidated_tax_detail = {}
for item_code, tax_data in tax_row_detail.items():
tax_data = ItemWiseTaxDetail(**tax_data)
if consolidated_tax_detail.get(item_code):
consolidated_tax_detail[item_code]["tax_amount"] += tax_data.tax_amount
consolidated_tax_detail[item_code]["net_amount"] += tax_data.net_amount
else:
consolidated_tax_detail.update({item_code: tax_data})
consolidate_tax_row.item_wise_tax_detail = json.dumps(consolidated_tax_detail)
def get_all_unconsolidated_invoices():
filters = {
"consolidated_invoice": ["in", ["", None]],

View File

@@ -164,36 +164,20 @@ class TestPOSInvoiceMergeLog(IntegrationTestCase):
inv.load_from_db()
consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
expected_item_wise_tax_details = [
{
"item_row": consolidated_invoice.items[0].name,
"tax_row": consolidated_invoice.taxes[0].name,
"rate": 9.0,
"amount": 9.0,
"taxable_amount": 100.0,
item_wise_tax_detail = json.loads(consolidated_invoice.get("taxes")[0].item_wise_tax_detail)
expected_item_wise_tax_detail = {
"_Test Item": {
"tax_rate": 9,
"tax_amount": 9,
"net_amount": 100,
},
{
"item_row": consolidated_invoice.items[1].name,
"tax_row": consolidated_invoice.taxes[0].name,
"rate": 5.0,
"amount": 5.0,
"taxable_amount": 100.0,
"_Test Item 2": {
"tax_rate": 5,
"tax_amount": 5,
"net_amount": 100,
},
]
actual = [
{
"item_row": d.item_row,
"tax_row": d.tax_row,
"rate": d.rate,
"amount": d.amount,
"taxable_amount": d.taxable_amount,
}
for d in consolidated_invoice.get("item_wise_tax_details")
]
self.assertEqual(actual, expected_item_wise_tax_details)
}
self.assertEqual(item_wise_tax_detail, expected_item_wise_tax_detail)
def test_consolidation_round_off_error_1(self):
"""

View File

@@ -43,19 +43,9 @@ class POSOpeningEntry(StatusUpdater):
self.set_status()
def validate_pos_profile_and_cashier(self):
if not frappe.db.exists("POS Profile", self.pos_profile):
frappe.throw(_("POS Profile {} does not exist.").format(self.pos_profile))
pos_profile_company, pos_profile_disabled = frappe.db.get_value(
"POS Profile", self.pos_profile, ["company", "disabled"]
)
if pos_profile_disabled:
frappe.throw(_("POS Profile {} is disabled.").format(frappe.bold(self.pos_profile)))
if self.company != pos_profile_company:
if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"):
frappe.throw(
_("POS Profile {} does not belong to company {}").format(self.pos_profile, self.company)
_("POS Profile {} does not belongs to company {}").format(self.pos_profile, self.company)
)
if not cint(frappe.db.get_value("User", self.user, "enabled")):

View File

@@ -40,12 +40,6 @@ class TestPOSOpeningEntry(IntegrationTestCase):
self.assertEqual(opening_entry.status, "Open")
self.assertNotEqual(opening_entry.docstatus, 0)
def test_pos_opening_entry_on_disabled_pos(self):
test_user, pos_profile = self.init_user_and_profile(disabled=1)
with self.assertRaises(frappe.ValidationError):
create_opening_entry(pos_profile, test_user.name)
def test_multiple_pos_opening_entries_for_same_pos_profile(self):
test_user, pos_profile = self.init_user_and_profile()
opening_entry = create_opening_entry(pos_profile, test_user.name)

View File

@@ -75,7 +75,6 @@ class POSProfile(Document):
# end: auto-generated types
def validate(self):
self.validate_disabled()
self.validate_default_profile()
self.validate_all_link_fields()
self.validate_duplicate_groups()
@@ -100,21 +99,6 @@ class POSProfile(Document):
title=_("Mandatory Accounting Dimension"),
)
def validate_disabled(self):
old_doc = self.get_doc_before_save()
if (
old_doc
and self.disabled
and old_doc.disabled != self.disabled
and frappe.db.exists("POS Opening Entry", {"pos_profile": self.name, "status": "Open"})
):
frappe.throw(
_("POS Profile {0} cannot be disabled as there are ongoing POS sessions.").format(
frappe.bold(self.name)
)
)
def validate_default_profile(self):
for row in self.applicable_for_users:
res = frappe.db.sql(

View File

@@ -4,7 +4,6 @@ import unittest
import frappe
from frappe.tests import IntegrationTestCase
from frappe.utils import cint
from erpnext.accounts.doctype.pos_profile.pos_profile import (
get_child_nodes,
@@ -39,50 +38,6 @@ class TestPOSProfile(IntegrationTestCase):
frappe.db.sql("delete from `tabPOS Profile`")
def test_disabled_pos_profile_creation(self):
make_pos_profile(name="_Test POS Profile 001", disabled=1)
pos_profile = frappe.get_doc("POS Profile", "_Test POS Profile 001")
if pos_profile:
self.assertEqual(pos_profile.disabled, 1)
def test_disabled_pos_profile_after_opening(self):
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry
test_user, pos_profile = init_user_and_profile()
if pos_profile:
create_opening_entry(pos_profile, test_user.name)
self.assertEqual(pos_profile.disabled, 0)
pos_profile.disabled = 1
self.assertRaises(frappe.ValidationError, pos_profile.save)
def test_disabled_pos_profile_after_completing_session(self):
from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import (
make_closing_entry_from_opening,
)
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import (
create_opening_entry,
)
test_user, pos_profile = init_user_and_profile()
if pos_profile:
opening_entry = create_opening_entry(pos_profile, test_user.name)
closing_entry = make_closing_entry_from_opening(opening_entry)
closing_entry.submit()
pos_profile.disabled = 1
pos_profile.save()
pos_profile.reload()
self.assertEqual(pos_profile.disabled, 1)
def get_customers_list(pos_profile=None):
if pos_profile is None:
@@ -162,7 +117,6 @@ def make_pos_profile(**args):
"write_off_account": args.write_off_account or "_Test Write Off - _TC",
"write_off_cost_center": args.write_off_cost_center or "_Test Write Off Cost Center - _TC",
"location": "Block 1" if not args.do_not_set_accounting_dimension else None,
"disabled": cint(args.disabled) or 0,
}
)

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Process Period Closing Voucher", {
refresh(frm) {
if (frm.doc.docstatus == 1 && ["Queued"].find((x) => x == frm.doc.status)) {
let execute_btn = __("Start");
frm.add_custom_button(execute_btn, () => {
frm.call({
method: "erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.start_pcv_processing",
args: {
docname: frm.doc.name,
},
}).then((r) => {
if (!r.exc) {
frappe.show_alert(__("Job Started"));
frm.reload_doc();
}
});
});
}
if (frm.doc.docstatus == 1 && ["Running"].find((x) => x == frm.doc.status)) {
let execute_btn = __("Pause");
frm.add_custom_button(execute_btn, () => {
frm.call({
method: "erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.pause_pcv_processing",
args: {
docname: frm.doc.name,
},
}).then((r) => {
if (!r.exc) {
frappe.show_alert(__("PCV Paused"));
frm.reload_doc();
}
});
});
}
if (frm.doc.docstatus == 1 && ["Paused"].find((x) => x == frm.doc.status)) {
let execute_btn = __("Resume");
frm.add_custom_button(execute_btn, () => {
frm.call({
method: "erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.resume_pcv_processing",
args: {
docname: frm.doc.name,
},
}).then((r) => {
if (!r.exc) {
frappe.show_alert(__("PCV Resumed"));
frm.reload_doc();
}
});
});
}
// progress bar
let progress = 0;
let normal_finished = frm.doc.normal_balances.filter((x) => x.status == "Completed").length;
let opening_finished = frm.doc.z_opening_balances.filter((x) => x.status == "Completed").length;
progress =
((normal_finished + opening_finished) /
(frm.doc.normal_balances.length + frm.doc.z_opening_balances.length)) *
100;
frm.dashboard.add_progress("Books closure progress", progress, "");
},
});

View File

@@ -1,113 +0,0 @@
{
"actions": [],
"autoname": "format:Process-PCV-{###}",
"creation": "2025-09-25 15:44:03.534699",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"parent_pcv",
"status",
"p_l_closing_balance",
"normal_balances",
"bs_closing_balance",
"z_opening_balances",
"amended_from"
],
"fields": [
{
"fieldname": "parent_pcv",
"fieldtype": "Link",
"in_list_view": 1,
"label": "PCV",
"options": "Period Closing Voucher",
"reqd": 1
},
{
"default": "Queued",
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"options": "Queued\nRunning\nPaused\nCompleted\nCancelled"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Process Period Closing Voucher",
"print_hide": 1,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "p_l_closing_balance",
"fieldtype": "JSON",
"label": "P&L Closing Balance",
"no_copy": 1
},
{
"fieldname": "normal_balances",
"fieldtype": "Table",
"label": "Dates to Process",
"no_copy": 1,
"options": "Process Period Closing Voucher Detail"
},
{
"fieldname": "z_opening_balances",
"fieldtype": "Table",
"label": "Opening Balances",
"no_copy": 1,
"options": "Process Period Closing Voucher Detail"
},
{
"fieldname": "bs_closing_balance",
"fieldtype": "JSON",
"label": "Balance Sheet Closing Balance"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2025-11-05 11:40:24.996403",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Period Closing Voucher",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"share": 1,
"submit": 1,
"write": 1
}
],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,559 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import copy
from datetime import timedelta
import frappe
from frappe import qb
from frappe.model.document import Document
from frappe.query_builder.functions import Count, Max, Min, Sum
from frappe.utils import add_days, flt, get_datetime
from frappe.utils.scheduler import is_scheduler_inactive
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
class ProcessPeriodClosingVoucher(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.process_period_closing_voucher_detail.process_period_closing_voucher_detail import (
ProcessPeriodClosingVoucherDetail,
)
amended_from: DF.Link | None
bs_closing_balance: DF.JSON | None
normal_balances: DF.Table[ProcessPeriodClosingVoucherDetail]
p_l_closing_balance: DF.JSON | None
parent_pcv: DF.Link
status: DF.Literal["Queued", "Running", "Paused", "Completed", "Cancelled"]
z_opening_balances: DF.Table[ProcessPeriodClosingVoucherDetail]
# end: auto-generated types
def validate(self):
self.status = "Queued"
self.populate_processing_tables()
def populate_processing_tables(self):
self.generate_pcv_dates()
self.generate_opening_balances_dates()
def get_dates(self, start, end):
return [start + timedelta(days=x) for x in range((end - start).days + 1)]
def generate_pcv_dates(self):
self.normal_balances = []
pcv = frappe.get_doc("Period Closing Voucher", self.parent_pcv)
dates = self.get_dates(get_datetime(pcv.period_start_date), get_datetime(pcv.period_end_date))
for x in dates:
self.append(
"normal_balances",
{"processing_date": x, "status": "Queued", "report_type": "Profit and Loss"},
)
self.append(
"normal_balances", {"processing_date": x, "status": "Queued", "report_type": "Balance Sheet"}
)
def generate_opening_balances_dates(self):
self.z_opening_balances = []
pcv = frappe.get_doc("Period Closing Voucher", self.parent_pcv)
if pcv.is_first_period_closing_voucher():
gl = qb.DocType("GL Entry")
min = qb.from_(gl).select(Min(gl.posting_date)).where(gl.company.eq(pcv.company)).run()[0][0]
max = qb.from_(gl).select(Max(gl.posting_date)).where(gl.company.eq(pcv.company)).run()[0][0]
dates = self.get_dates(get_datetime(min), get_datetime(max))
for x in dates:
self.append(
"z_opening_balances",
{"processing_date": x, "status": "Queued", "report_type": "Balance Sheet"},
)
def on_submit(self):
start_pcv_processing(self.name)
def on_cancel(self):
cancel_pcv_processing(self.name)
@frappe.whitelist()
def start_pcv_processing(docname: str):
if frappe.db.get_value("Process Period Closing Voucher", docname, "status") in ["Queued", "Running"]:
frappe.db.set_value("Process Period Closing Voucher", docname, "status", "Running")
if normal_balances := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
fields=["processing_date", "report_type", "parentfield"],
order_by="parentfield, idx, processing_date",
limit=4,
):
if not is_scheduler_inactive():
for x in normal_balances:
frappe.db.set_value(
"Process Period Closing Voucher Detail",
{
"processing_date": x.processing_date,
"parent": docname,
"report_type": x.report_type,
"parentfield": x.parentfield,
},
"status",
"Running",
)
frappe.enqueue(
method="erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.process_individual_date",
queue="long",
timeout="3600",
is_async=True,
enqueue_after_commit=True,
docname=docname,
date=x.processing_date,
report_type=x.report_type,
parentfield=x.parentfield,
)
else:
frappe.db.set_value("Process Period Closing Voucher", docname, "status", "Completed")
@frappe.whitelist()
def pause_pcv_processing(docname: str):
ppcv = qb.DocType("Process Period Closing Voucher")
qb.update(ppcv).set(ppcv.status, "Paused").where(ppcv.name.eq(docname)).run()
if queued_dates := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
pluck="name",
):
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
qb.update(ppcvd).set(ppcvd.status, "Paused").where(ppcvd.name.isin(queued_dates)).run()
@frappe.whitelist()
def cancel_pcv_processing(docname: str):
ppcv = qb.DocType("Process Period Closing Voucher")
qb.update(ppcv).set(ppcv.status, "Cancelled").where(ppcv.name.eq(docname)).run()
if queued_dates := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
pluck="name",
):
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
qb.update(ppcvd).set(ppcvd.status, "Cancelled").where(ppcvd.name.isin(queued_dates)).run()
@frappe.whitelist()
def resume_pcv_processing(docname: str):
ppcv = qb.DocType("Process Period Closing Voucher")
qb.update(ppcv).set(ppcv.status, "Running").where(ppcv.name.eq(docname)).run()
if paused_dates := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Paused"},
pluck="name",
):
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
qb.update(ppcvd).set(ppcvd.status, "Queued").where(ppcvd.name.isin(paused_dates)).run()
start_pcv_processing(docname)
def update_default_dimensions(dimension_fields, gl_entry, dimension_values):
for i, dimension in enumerate(dimension_fields):
gl_entry[dimension] = dimension_values[i]
def get_gle_for_pl_account(pcv, acc, balances, dimensions):
balance_in_account_currency = flt(balances.debit_in_account_currency) - flt(
balances.credit_in_account_currency
)
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
gl_entry = frappe._dict(
{
"company": pcv.company,
"posting_date": pcv.period_end_date,
"account": acc,
"account_currency": balances.account_currency,
"debit_in_account_currency": abs(balance_in_account_currency)
if balance_in_account_currency < 0
else 0,
"debit": abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0,
"credit_in_account_currency": abs(balance_in_account_currency)
if balance_in_account_currency > 0
else 0,
"credit": abs(balance_in_company_currency) if balance_in_company_currency > 0 else 0,
"is_period_closing_voucher_entry": 1,
"voucher_type": "Period Closing Voucher",
"voucher_no": pcv.name,
"fiscal_year": pcv.fiscal_year,
"remarks": pcv.remarks,
"is_opening": "No",
}
)
# update dimensions
update_default_dimensions(get_dimensions(), gl_entry, dimensions)
return gl_entry
def get_gle_for_closing_account(pcv, dimension_balance, dimensions):
balance_in_company_currency = flt(dimension_balance.balance_in_company_currency)
debit = balance_in_company_currency if balance_in_company_currency > 0 else 0
credit = abs(balance_in_company_currency) if balance_in_company_currency < 0 else 0
gl_entry = frappe._dict(
{
"company": pcv.company,
"posting_date": pcv.period_end_date,
"account": pcv.closing_account_head,
"account_currency": frappe.db.get_value("Account", pcv.closing_account_head, "account_currency"),
"debit_in_account_currency": debit,
"debit": debit,
"credit_in_account_currency": credit,
"credit": credit,
"is_period_closing_voucher_entry": 1,
"voucher_type": "Period Closing Voucher",
"voucher_no": pcv.name,
"fiscal_year": pcv.fiscal_year,
"remarks": pcv.remarks,
"is_opening": "No",
}
)
# update dimensions
update_default_dimensions(get_dimensions(), gl_entry, dimensions)
return gl_entry
@frappe.whitelist()
def schedule_next_date(docname: str):
if to_process := frappe.db.get_all(
"Process Period Closing Voucher Detail",
filters={"parent": docname, "status": "Queued"},
fields=["processing_date", "report_type", "parentfield"],
order_by="parentfield, idx, processing_date",
limit=1,
):
if not is_scheduler_inactive():
frappe.db.set_value(
"Process Period Closing Voucher Detail",
{
"processing_date": to_process[0].processing_date,
"parent": docname,
"report_type": to_process[0].report_type,
"parentfield": to_process[0].parentfield,
},
"status",
"Running",
)
frappe.enqueue(
method="erpnext.accounts.doctype.process_period_closing_voucher.process_period_closing_voucher.process_individual_date",
queue="long",
timeout="3600",
is_async=True,
enqueue_after_commit=True,
docname=docname,
date=to_process[0].processing_date,
report_type=to_process[0].report_type,
parentfield=to_process[0].parentfield,
)
else:
ppcvd = qb.DocType("Process Period Closing Voucher Detail")
total_no_of_dates = (
qb.from_(ppcvd).select(Count(ppcvd.star)).where(ppcvd.parent.eq(docname)).run()[0][0]
)
completed = (
qb.from_(ppcvd)
.select(Count(ppcvd.star))
.where(ppcvd.parent.eq(docname) & ppcvd.status.eq("Completed"))
.run()[0][0]
)
# Ensure both normal and opening balances are processed for all dates
if total_no_of_dates == completed:
summarize_and_post_ledger_entries(docname)
def make_dict_json_compliant(dimension_wise_balance) -> dict:
"""
convert tuple -> str
JSON doesn't support dictionary with tuple keys
"""
converted_dict = {}
for k, v in dimension_wise_balance.items():
str_key = [str(x) for x in k]
str_key = ",".join(str_key)
converted_dict[str_key] = v
return converted_dict
def get_consolidated_gles(balances, report_type) -> list:
gl_entries = []
for x in balances:
if x.report_type == report_type:
closing_balances = [frappe._dict(gle) for gle in frappe.json.loads(x.closing_balance)]
gl_entries.extend(closing_balances)
return gl_entries
def get_gl_entries(docname):
"""
Calculate total closing balance of all P&L accounts across PCV start and end date
"""
ppcv = frappe.get_doc("Process Period Closing Voucher", docname)
# calculate balance
gl_entries = get_consolidated_gles(ppcv.normal_balances, "Profit and Loss")
pl_dimension_wise_acc_balance = build_dimension_wise_balance_dict(gl_entries)
# save
json_dict = make_dict_json_compliant(pl_dimension_wise_acc_balance)
frappe.db.set_value(
"Process Period Closing Voucher", docname, "p_l_closing_balance", frappe.json.dumps(json_dict)
)
# build gl map
pcv = frappe.get_doc("Period Closing Voucher", ppcv.parent_pcv)
pl_accounts_reverse_gle = []
closing_account_gle = []
for dimensions, account_balances in pl_dimension_wise_acc_balance.items():
for acc, balances in account_balances.items():
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
if balance_in_company_currency:
pl_accounts_reverse_gle.append(get_gle_for_pl_account(pcv, acc, balances, dimensions))
closing_account_gle.append(get_gle_for_closing_account(pcv, account_balances["balances"], dimensions))
return pl_accounts_reverse_gle, closing_account_gle
def calculate_balance_sheet_balance(docname):
"""
Calculate total closing balance of all P&L accounts across PCV start and end date.
If it is first PCV, opening entries are also considered
"""
ppcv = frappe.get_doc("Process Period Closing Voucher", docname)
gl_entries = get_consolidated_gles(ppcv.normal_balances + ppcv.z_opening_balances, "Balance Sheet")
# build dimension wise dictionary from all GLE's
bs_dimension_wise_acc_balance = build_dimension_wise_balance_dict(gl_entries)
# save
json_dict = make_dict_json_compliant(bs_dimension_wise_acc_balance)
frappe.db.set_value(
"Process Period Closing Voucher", docname, "bs_closing_balance", frappe.json.dumps(json_dict)
)
return bs_dimension_wise_acc_balance
def get_p_l_closing_entries(pl_gles, pcv):
pl_closing_entries = copy.deepcopy(pl_gles)
for d in pl_gles:
# reverse debit and credit
gle_copy = copy.deepcopy(d)
gle_copy.debit = d.credit
gle_copy.credit = d.debit
gle_copy.debit_in_account_currency = d.credit_in_account_currency
gle_copy.credit_in_account_currency = d.debit_in_account_currency
gle_copy.is_period_closing_voucher_entry = 0
gle_copy.period_closing_voucher = pcv.name
pl_closing_entries.append(gle_copy)
return pl_closing_entries
def get_bs_closing_entries(dimension_wise_balance, pcv):
closing_entries = []
for dimensions, account_balances in dimension_wise_balance.items():
for acc, balances in account_balances.items():
balance_in_company_currency = flt(balances.debit) - flt(balances.credit)
if acc != "balances" and balance_in_company_currency:
closing_entries.append(get_closing_entry(pcv, acc, balances, dimensions))
return closing_entries
def get_closing_account_closing_entry(closing_account_gle, pcv):
closing_entries_for_closing_account = copy.deepcopy(closing_account_gle)
for d in closing_entries_for_closing_account:
d.period_closing_voucher = pcv.name
return closing_entries_for_closing_account
def summarize_and_post_ledger_entries(docname):
# P&L accounts
pl_accounts_reverse_gle, closing_account_gle = get_gl_entries(docname)
gl_entries = pl_accounts_reverse_gle + closing_account_gle
from erpnext.accounts.general_ledger import make_gl_entries
if gl_entries:
make_gl_entries(gl_entries, merge_entries=False)
pcv_name = frappe.db.get_value("Process Period Closing Voucher", docname, "parent_pcv")
pcv = frappe.get_doc("Period Closing Voucher", pcv_name)
# Balance sheet accounts
bs_dimension_wise_acc_balance = calculate_balance_sheet_balance(docname)
pl_closing_entries = get_p_l_closing_entries(pl_accounts_reverse_gle, pcv)
bs_closing_entries = get_bs_closing_entries(bs_dimension_wise_acc_balance, pcv)
closing_entries_for_closing_account = get_closing_account_closing_entry(closing_account_gle, pcv)
closing_entries = pl_closing_entries + bs_closing_entries + closing_entries_for_closing_account
make_closing_entries(closing_entries, pcv.name, pcv.company, pcv.period_end_date)
frappe.db.set_value("Period Closing Voucher", pcv.name, "gle_processing_status", "Completed")
frappe.db.set_value("Process Period Closing Voucher", docname, "status", "Completed")
def get_closing_entry(pcv, account, balances, dimensions):
closing_entry = frappe._dict(
{
"company": pcv.company,
"closing_date": pcv.period_end_date,
"period_closing_voucher": pcv.name,
"account": account,
"account_currency": balances.account_currency,
"debit_in_account_currency": flt(balances.debit_in_account_currency),
"debit": flt(balances.debit),
"credit_in_account_currency": flt(balances.credit_in_account_currency),
"credit": flt(balances.credit),
"is_period_closing_voucher_entry": 0,
}
)
# update dimensions
update_default_dimensions(get_dimensions(), closing_entry, dimensions)
return closing_entry
def get_dimensions():
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
default_dimensions = ["cost_center", "finance_book", "project"]
dimensions = default_dimensions + get_accounting_dimensions()
return dimensions
def get_dimension_key(res):
return tuple([res.get(dimension) for dimension in get_dimensions()])
def build_dimension_wise_balance_dict(gl_entries):
dimension_balances = frappe._dict()
for x in gl_entries:
dimension_key = get_dimension_key(x)
dimension_balances.setdefault(dimension_key, frappe._dict()).setdefault(
x.account,
frappe._dict(
{
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
"debit": 0,
"credit": 0,
"account_currency": x.account_currency,
}
),
)
dimension_balances[dimension_key][x.account].debit_in_account_currency += flt(
x.debit_in_account_currency
)
dimension_balances[dimension_key][x.account].credit_in_account_currency += flt(
x.credit_in_account_currency
)
dimension_balances[dimension_key][x.account].debit += flt(x.debit)
dimension_balances[dimension_key][x.account].credit += flt(x.credit)
# dimension-wise total balances
dimension_balances[dimension_key].setdefault(
"balances",
frappe._dict(
{
"balance_in_account_currency": 0,
"balance_in_company_currency": 0,
}
),
)
balance_in_account_currency = flt(x.debit_in_account_currency) - flt(x.credit_in_account_currency)
balance_in_company_currency = flt(x.debit) - flt(x.credit)
dimension_balances[dimension_key][
"balances"
].balance_in_account_currency += balance_in_account_currency
dimension_balances[dimension_key][
"balances"
].balance_in_company_currency += balance_in_company_currency
return dimension_balances
def process_individual_date(docname: str, date, report_type, parentfield):
current_date_status = frappe.db.get_value(
"Process Period Closing Voucher Detail",
{"processing_date": date, "report_type": report_type, "parentfield": parentfield},
"status",
)
if current_date_status != "Running":
return
pcv_name = frappe.db.get_value("Process Period Closing Voucher", docname, "parent_pcv")
company = frappe.db.get_value("Period Closing Voucher", pcv_name, "company")
dimensions = get_dimensions()
accounts = frappe.db.get_all(
"Account", filters={"company": company, "report_type": report_type}, pluck="name"
)
# summarize
gle = qb.DocType("GL Entry")
query = qb.from_(gle).select(gle.account)
for dim in dimensions:
query = query.select(gle[dim])
query = query.select(
Sum(gle.debit).as_("debit"),
Sum(gle.credit).as_("credit"),
Sum(gle.debit_in_account_currency).as_("debit_in_account_currency"),
Sum(gle.credit_in_account_currency).as_("credit_in_account_currency"),
gle.account_currency,
).where(
(gle.company.eq(company))
& (gle.is_cancelled.eq(0))
& (gle.posting_date.eq(date))
& (gle.account.isin(accounts))
)
if parentfield == "z_opening_balances":
query = query.where(gle.is_opening.eq("Yes"))
query = query.groupby(gle.account)
for dim in dimensions:
query = query.groupby(gle[dim])
res = query.run(as_dict=True)
# save results
frappe.db.set_value(
"Process Period Closing Voucher Detail",
{"processing_date": date, "parent": docname, "report_type": report_type, "parentfield": parentfield},
"closing_balance",
frappe.json.dumps(res),
)
frappe.db.set_value(
"Process Period Closing Voucher Detail",
{"processing_date": date, "parent": docname, "report_type": report_type, "parentfield": parentfield},
"status",
"Completed",
)
# chain call
schedule_next_date(docname)

View File

@@ -1,20 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests import IntegrationTestCase
# On IntegrationTestCase, the doctype test records and all
# link-field test record dependencies are recursively loaded
# Use these module variables to add/remove to/from that list
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
class IntegrationTestProcessPeriodClosingVoucher(IntegrationTestCase):
"""
Integration tests for ProcessPeriodClosingVoucher.
Use this class for testing interactions between multiple components.
"""
pass

View File

@@ -1,58 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2025-10-01 15:58:17.544153",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"processing_date",
"report_type",
"status",
"closing_balance"
],
"fields": [
{
"fieldname": "processing_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Processing Date"
},
{
"default": "Queued",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Queued\nRunning\nPaused\nCompleted\nCancelled"
},
{
"fieldname": "closing_balance",
"fieldtype": "JSON",
"in_list_view": 1,
"label": "Closing Balance"
},
{
"default": "Profit and Loss",
"fieldname": "report_type",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Report Type",
"options": "Profit and Loss\nBalance Sheet"
}
],
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-10-20 12:03:59.106931",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Period Closing Voucher Detail",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"rows_threshold_for_grid_search": 20,
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}

View File

@@ -1,26 +0,0 @@
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class ProcessPeriodClosingVoucherDetail(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
closing_balance: DF.JSON | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
processing_date: DF.Date | None
report_type: DF.Literal["Profit and Loss", "Balance Sheet"]
status: DF.Literal["Queued", "Running", "Paused", "Completed", "Cancelled"]
# end: auto-generated types
pass

View File

@@ -71,6 +71,14 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
if (this.frm.doc.supplier && this.frm.doc.__islocal) {
this.frm.trigger("supplier");
}
this.frm.set_query("supplier", function () {
return {
filters: {
is_transporter: 0,
},
};
});
}
refresh(doc) {

View File

@@ -112,7 +112,6 @@
"tax_withheld_vouchers",
"sec_tax_breakup",
"other_charges_calculation",
"item_wise_tax_details",
"pricing_rule_details",
"pricing_rules",
"raw_materials_supplied",
@@ -1671,14 +1670,6 @@
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "item_wise_tax_details",
"fieldtype": "Table",
"hidden": 1,
"label": "Item Wise Tax Details",
"no_copy": 1,
"options": "Item Wise Tax Detail"
}
],
"grid_page_length": 50,

View File

@@ -40,6 +40,7 @@ from erpnext.assets.doctype.asset_category.asset_category import get_asset_categ
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.controllers.accounts_controller import validate_account_head
from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock import get_warehouse_account_map
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
update_billed_amount_based_on_po,
)
@@ -62,7 +63,6 @@ class PurchaseInvoice(BuyingController):
from frappe.types import DF
from erpnext.accounts.doctype.advance_tax.advance_tax import AdvanceTax
from erpnext.accounts.doctype.item_wise_tax_detail.item_wise_tax_detail import ItemWiseTaxDetail
from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule
from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail
from erpnext.accounts.doctype.purchase_invoice_advance.purchase_invoice_advance import (
@@ -137,7 +137,6 @@ class PurchaseInvoice(BuyingController):
is_paid: DF.Check
is_return: DF.Check
is_subcontracted: DF.Check
item_wise_tax_details: DF.Table[ItemWiseTaxDetail]
items: DF.Table[PurchaseInvoiceItem]
language: DF.Data | None
letter_head: DF.Link | None
@@ -461,12 +460,11 @@ class PurchaseInvoice(BuyingController):
self.asset_received_but_not_billed = None
inventory_account_map = {}
if self.update_stock:
self.validate_item_code()
self.validate_warehouse(for_validate)
if auto_accounting_for_stock:
inventory_account_map = self.get_inventory_account_map()
warehouse_account = get_warehouse_account_map(self.company)
for item in self.get("items"):
# in case of auto inventory accounting,
@@ -483,19 +481,21 @@ class PurchaseInvoice(BuyingController):
)
):
if self.update_stock and item.warehouse and (not item.from_warehouse):
_inv_dict = self.get_inventory_account_dict(item, inventory_account_map)
if for_validate and item.expense_account and item.expense_account != _inv_dict["account"]:
if (
for_validate
and item.expense_account
and item.expense_account != warehouse_account[item.warehouse]["account"]
):
msg = _(
"Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account"
).format(
item.idx,
frappe.bold(_inv_dict["account"]),
frappe.bold(warehouse_account[item.warehouse]["account"]),
frappe.bold(item.expense_account),
frappe.bold(item.warehouse),
)
frappe.msgprint(msg, title=_("Expense Head Changed"))
item.expense_account = _inv_dict["account"]
item.expense_account = warehouse_account[item.warehouse]["account"]
else:
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
if item.purchase_receipt:
@@ -857,7 +857,7 @@ class PurchaseInvoice(BuyingController):
party=self.supplier,
)
def get_gl_entries(self, inventory_account_map=None):
def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if self.auto_accounting_for_stock:
@@ -947,7 +947,7 @@ class PurchaseInvoice(BuyingController):
# item gl entries
stock_items = self.get_stock_items()
if self.update_stock and self.auto_accounting_for_stock:
inventory_account_map = self.get_inventory_account_map()
warehouse_account = get_warehouse_account_map(self.company)
landed_cost_entries = self.get_item_account_wise_lcv_entries()
@@ -997,24 +997,18 @@ class PurchaseInvoice(BuyingController):
)
if item.from_warehouse:
_inv_dict = self.get_inventory_account_dict(item, inventory_account_map)
_inv_dict_from_warehouse = self.get_inventory_account_dict(
item, inventory_account_map, "from_warehouse"
)
gl_entries.append(
self.get_gl_dict(
{
"account": _inv_dict["account"],
"against": _inv_dict_from_warehouse["account"],
"account": warehouse_account[item.warehouse]["account"],
"against": warehouse_account[item.from_warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": warehouse_debit_amount,
"debit_in_transaction_currency": item.net_amount,
},
_inv_dict["account_currency"],
warehouse_account[item.warehouse]["account_currency"],
item=item,
)
)
@@ -1027,15 +1021,15 @@ class PurchaseInvoice(BuyingController):
gl_entries.append(
self.get_gl_dict(
{
"account": _inv_dict_from_warehouse["account"],
"against": _inv_dict["account"],
"account": warehouse_account[item.from_warehouse]["account"],
"against": warehouse_account[item.warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(credit_amount, item.precision("base_net_amount")),
"debit_in_transaction_currency": item.net_amount,
},
_inv_dict_from_warehouse["account_currency"],
warehouse_account[item.from_warehouse]["account_currency"],
item=item,
)
)
@@ -1103,19 +1097,15 @@ class PurchaseInvoice(BuyingController):
# sub-contracting warehouse
if flt(item.rm_supp_cost):
supplier_wh_dict = self.get_inventory_account_dict(
item, inventory_account_map, "supplier_warehouse"
)
supplier_inventory_account = supplier_wh_dict["account"]
if not supplier_inventory_account:
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
if not supplier_warehouse_account:
frappe.throw(
_("Please set account in Warehouse {0}").format(self.supplier_warehouse)
)
gl_entries.append(
self.get_gl_dict(
{
"account": supplier_inventory_account,
"account": supplier_warehouse_account,
"against": item.expense_account,
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1123,7 +1113,7 @@ class PurchaseInvoice(BuyingController):
"credit": flt(item.rm_supp_cost),
"credit_in_transaction_currency": item.net_amount,
},
supplier_wh_dict["account_currency"],
warehouse_account[self.supplier_warehouse]["account_currency"],
item=item,
)
)

View File

@@ -912,8 +912,7 @@
"label": "Rejected Serial and Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1,
"search_index": 1
"print_hide": 1
},
{
"fieldname": "wip_composite_asset",
@@ -985,7 +984,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-10-14 13:00:54.441511",
"modified": "2025-03-07 10:21:59.960021",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
@@ -996,4 +995,4 @@
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -34,7 +34,8 @@
"base_net_amount",
"base_tax_amount",
"base_total",
"base_tax_amount_after_discount_amount"
"base_tax_amount_after_discount_amount",
"item_wise_tax_detail"
],
"fields": [
{
@@ -195,6 +196,16 @@
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "item_wise_tax_detail",
"fieldtype": "Code",
"hidden": 1,
"label": "Item Wise Tax Detail",
"oldfieldname": "item_wise_tax_detail",
"oldfieldtype": "Small Text",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
@@ -268,7 +279,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-07-24 15:08:44.433022",
"modified": "2025-04-15 13:14:48.936047",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",

View File

@@ -35,6 +35,7 @@ class PurchaseTaxesandCharges(Document):
included_in_paid_amount: DF.Check
included_in_print_rate: DF.Check
is_tax_withholding_account: DF.Check
item_wise_tax_detail: DF.Code | None
net_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data

View File

@@ -101,8 +101,8 @@ class RepostAccountingLedger(Document):
if doc.doctype in ["Payment Entry", "Journal Entry"]:
gle_map = doc.build_gl_map()
elif doc.doctype == "Purchase Receipt":
inventory_account_map = doc.get_inventory_account_map()
gle_map = doc.get_gl_entries(inventory_account_map)
warehouse_account_map = get_warehouse_account_map(doc.company)
gle_map = doc.get_gl_entries(warehouse_account_map)
else:
gle_map = doc.get_gl_entries()

View File

@@ -256,18 +256,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
sales_order_btn() {
var me = this;
let filters = {
docstatus: 1,
status: ["not in", ["Closed", "On Hold"]],
per_billed: ["<", 99.99],
company: me.frm.doc.company,
};
if (me.frm.doc.has_subcontracted) {
filters.is_subcontracted = 1;
}
this.$sales_order_btn = this.frm.add_custom_button(
__("Sales Order"),
function () {
@@ -278,7 +266,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
setters: {
customer: me.frm.doc.customer || undefined,
},
get_query_filters: filters,
get_query_filters: {
docstatus: 1,
status: ["not in", ["Closed", "On Hold"]],
per_billed: ["<", 99.99],
company: me.frm.doc.company,
},
allow_child_item_selection: true,
child_fieldname: "items",
child_columns: ["item_code", "item_name", "qty", "amount", "billed_amt"],
@@ -805,15 +798,6 @@ frappe.ui.form.on("Sales Invoice", {
},
};
});
frm.set_query("sales_person", "sales_team", function () {
return {
filters: {
is_group: 0,
enabled: 1,
},
};
});
},
onload: function (frm) {
frm.redemption_conversion_factor = null;
@@ -1110,9 +1094,6 @@ frappe.ui.form.on("Sales Invoice", {
if (frm.doc.is_debit_note) {
frm.set_df_property("return_against", "label", __("Adjustment Against"));
}
frm.set_df_property("update_stock", "read_only", frm.doc.has_subcontracted);
frm.toggle_display("update_stock", !frm.doc.has_subcontracted);
},
});

View File

@@ -31,7 +31,6 @@
"amended_from",
"is_created_using_pos",
"pos_closing_entry",
"has_subcontracted",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
@@ -101,7 +100,6 @@
"discount_amount",
"sec_tax_breakup",
"other_charges_calculation",
"item_wise_tax_details",
"pricing_rule_details",
"pricing_rules",
"packing_list",
@@ -2231,22 +2229,6 @@
"fieldtype": "Data",
"is_virtual": 1,
"label": "Last Scanned Warehouse"
},
{
"default": "0",
"fieldname": "has_subcontracted",
"fieldtype": "Check",
"hidden": 1,
"label": "Has Subcontracted",
"read_only": 1
},
{
"fieldname": "item_wise_tax_details",
"fieldtype": "Table",
"hidden": 1,
"label": "Item Wise Tax Details",
"no_copy": 1,
"options": "Item Wise Tax Detail"
}
],
"grid_page_length": 50,
@@ -2260,7 +2242,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2025-10-09 14:48:59.472826",
"modified": "2025-09-09 14:48:59.472826",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -8,7 +8,6 @@ from frappe import _, msgprint, throw
from frappe.contacts.doctype.address.address import get_address_display
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
from frappe.query_builder import Case
from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
from frappe.utils.data import comma_and
@@ -32,6 +31,7 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
update_voucher_outstanding,
)
@@ -65,7 +65,6 @@ class SalesInvoice(SellingController):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.item_wise_tax_detail.item_wise_tax_detail import ItemWiseTaxDetail
from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule
from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail
from erpnext.accounts.doctype.sales_invoice_advance.sales_invoice_advance import SalesInvoiceAdvance
@@ -132,7 +131,6 @@ class SalesInvoice(SellingController):
from_date: DF.Date | None
grand_total: DF.Currency
group_same_items: DF.Check
has_subcontracted: DF.Check
ignore_default_payment_terms_template: DF.Check
ignore_pricing_rule: DF.Check
in_words: DF.SmallText | None
@@ -147,7 +145,6 @@ class SalesInvoice(SellingController):
is_opening: DF.Literal["No", "Yes"]
is_pos: DF.Check
is_return: DF.Check
item_wise_tax_details: DF.Table[ItemWiseTaxDetail]
items: DF.Table[SalesInvoiceItem]
language: DF.Link | None
letter_head: DF.Link | None
@@ -339,8 +336,6 @@ class SalesInvoice(SellingController):
self.validate_auto_set_posting_time()
super().validate()
self.is_subcontracted()
if not (self.is_pos or self.is_debit_note):
self.so_dn_required()
@@ -413,8 +408,6 @@ class SalesInvoice(SellingController):
self.allow_write_off_only_on_pos()
self.reset_default_field_value("set_warehouse", "items", "warehouse")
self.validate_subcontracted_sales_order()
self.validate_scio_self_rm_qty()
def validate_accounts(self):
self.validate_write_off_account()
@@ -581,7 +574,6 @@ class SalesInvoice(SellingController):
self.apply_loyalty_points()
self.process_common_party_accounting()
self.update_billed_qty_in_scio()
def validate_pos_return(self):
if self.is_consolidated:
@@ -712,8 +704,6 @@ class SalesInvoice(SellingController):
):
self.cancel_pos_invoice_credit_note_generated_during_sales_invoice_mode()
self.update_billed_qty_in_scio()
def update_status_updater_args(self):
if not cint(self.update_stock):
return
@@ -848,27 +838,6 @@ class SalesInvoice(SellingController):
timesheet.set_status()
timesheet.db_update_all()
def update_billed_qty_in_scio(self):
if self.is_return:
return
table = frappe.qb.DocType("Subcontracting Inward Order Received Item")
data = frappe._dict(
{
item.scio_detail: item.stock_qty if self._action == "submit" else -item.stock_qty
for item in self.items
if item.scio_detail
}
)
if data:
case_expr = Case()
for name, qty in data.items():
case_expr = case_expr.when(table.name == name, table.billed_qty + qty)
frappe.qb.update(table).set(table.billed_qty, case_expr).where(
(table.name.isin(list(data.keys()))) & (table.docstatus == 1)
).run()
def update_time_sheet_detail(self, timesheet, args, sales_invoice):
for data in timesheet.time_logs:
if (
@@ -1261,48 +1230,6 @@ class SalesInvoice(SellingController):
if not self.is_pos and self.write_off_account:
self.write_off_account = None
def validate_subcontracted_sales_order(self):
if self.has_subcontracted:
if [item for item in self.items if not item.sales_order and not item.scio_detail]:
frappe.throw(
_(
"All items must be linked to a Sales Order or Subcontracting Inward Order for this Sales Invoice."
)
)
if not all(
frappe.get_all(
"Sales Order",
{"name": ["in", [item.sales_order for item in self.items if item.sales_order]]},
pluck="is_subcontracted",
)
):
frappe.throw(_("All linked Sales Orders must be subcontracted."))
def validate_scio_self_rm_qty(self):
self_rms = [item for item in self.items if item.scio_detail]
if self_rms:
table = frappe.qb.DocType("Subcontracting Inward Order Received Item")
query = (
frappe.qb.from_(table)
.select(table.required_qty, table.consumed_qty, table.billed_qty, table.name)
.where((table.docstatus == 1) & (table.name.isin([item.scio_detail for item in self_rms])))
)
result = query.run(as_dict=True)
data = {item.name: item for item in result}
for item in self_rms:
row = data.get(item.scio_detail)
max_qty = max(row.required_qty, row.consumed_qty) - row.billed_qty
if item.stock_qty > max_qty:
frappe.throw(
_("Row #{0}: Stock quantity {1} ({2}) for item {3} cannot exceed {4}").format(
item.idx,
item.stock_qty,
item.stock_uom,
get_link_to_form("Item", item.item_code),
frappe.bold(max_qty),
)
)
def validate_write_off_account(self):
if flt(self.write_off_amount) and not self.write_off_account:
self.write_off_account = frappe.get_cached_value("Company", self.company, "write_off_account")
@@ -1552,7 +1479,7 @@ class SalesInvoice(SellingController):
elif self.docstatus == 2 and cint(self.update_stock) and cint(auto_accounting_for_stock):
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, inventory_account_map=None):
def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries
gl_entries = []
@@ -2224,23 +2151,6 @@ class SalesInvoice(SellingController):
if update:
self.db_set("status", self.status, update_modified=update_modified)
@frappe.whitelist()
def is_subcontracted(self):
if not self.has_subcontracted:
self.has_subcontracted = bool(
frappe.get_cached_value(
"Sales Order",
{
"name": ["in", [item.sales_order for item in self.items if item.sales_order]],
"is_subcontracted": 1,
},
"name",
)
)
if self.has_subcontracted:
self.update_stock = 0
return self.has_subcontracted
def get_total_in_party_account_currency(doc):
total_fieldname = "grand_total" if doc.disable_rounded_total else "rounded_total"
@@ -2442,7 +2352,7 @@ def make_delivery_note(source_name, target_doc=None):
"cost_center": "cost_center",
},
"postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier != 1 and not doc.scio_detail,
"condition": lambda doc: doc.delivered_by_supplier != 1,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {
@@ -2702,9 +2612,6 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
target.purchase_order = source.purchase_order
target.po_detail = source.purchase_order_item
if (source.get("serial_no") or source.get("batch_no")) and not source.get("serial_and_batch_bundle"):
target.use_serial_batch_fields = 1
item_field_map = {
"doctype": target_doctype + " Item",
"field_no_map": ["income_account", "expense_account", "cost_center", "warehouse"],

View File

@@ -2077,12 +2077,12 @@ class TestSalesInvoice(ERPNextTestSuite):
{
"item": "_Test Item",
"taxable_amount": 10000.0,
"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0, "taxable_amount": 10000.0},
"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0, "net_amount": 10000.0},
},
{
"item": "_Test Item 2",
"taxable_amount": 5000.0,
"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0, "taxable_amount": 5000.0},
"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0, "net_amount": 5000.0},
},
]
@@ -3980,29 +3980,29 @@ class TestSalesInvoice(ERPNextTestSuite):
target_doc=si,
args=json.dumps({"customer": dn1.customer, "merge_taxes": 1, "filtered_children": []}),
)
si.save()
si.save().submit()
expected = [
{
"charge_type": "Actual",
"account_head": "Freight and Forwarding Charges - _TC",
"tax_amount": 120.0,
"total": 1620.0,
"base_total": 1620.0,
"total": 1520.0,
"base_total": 1520.0,
},
{
"charge_type": "Actual",
"account_head": "Marketing Expenses - _TC",
"tax_amount": 150.0,
"total": 1770.0,
"base_total": 1770.0,
"total": 1670.0,
"base_total": 1670.0,
},
{
"charge_type": "Actual",
"account_head": "Miscellaneous Expenses - _TC",
"tax_amount": 60.0,
"total": 1830.0,
"base_total": 1830.0,
"total": 1610.0,
"base_total": 1610.0,
},
]
actual = [

View File

@@ -109,7 +109,6 @@
"column_break_vwhb",
"pos_invoice",
"pos_invoice_item",
"scio_detail",
"internal_transfer_section",
"purchase_order",
"column_break_92",
@@ -979,20 +978,13 @@
"options": "POS Invoice",
"print_hide": 1,
"search_index": 1
},
{
"fieldname": "scio_detail",
"fieldtype": "Data",
"hidden": 1,
"label": "SCIO Detail",
"read_only": 1
}
],
"grid_page_length": 50,
"idx": 1,
"istable": 1,
"links": [],
"modified": "2025-09-04 11:08:25.583561",
"modified": "2025-03-07 10:25:30.275246",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
@@ -1003,4 +995,4 @@
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -84,7 +84,6 @@ class SalesInvoiceItem(Document):
rate_with_margin: DF.Currency
sales_invoice_item: DF.Data | None
sales_order: DF.Link | None
scio_detail: DF.Data | None
serial_and_batch_bundle: DF.Link | None
serial_no: DF.Text | None
service_end_date: DF.Date | None

View File

@@ -31,6 +31,7 @@
"base_tax_amount",
"base_total",
"base_tax_amount_after_discount_amount",
"item_wise_tax_detail",
"dont_recompute_tax"
],
"fields": [
@@ -173,6 +174,15 @@
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "item_wise_tax_detail",
"fieldtype": "Code",
"hidden": 1,
"label": "Item Wise Tax Detail",
"oldfieldname": "item_wise_tax_detail",
"oldfieldtype": "Small Text",
"read_only": 1
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
@@ -247,14 +257,13 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2025-07-24 15:08:34.381704",
"modified": "2024-11-22 19:17:31.898467",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges",
"owner": "Administrator",
"permissions": [],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "ASC",
"states": []
}
}

View File

@@ -33,6 +33,7 @@ class SalesTaxesandCharges(Document):
dont_recompute_tax: DF.Check
included_in_paid_amount: DF.Check
included_in_print_rate: DF.Check
item_wise_tax_detail: DF.Code | None
net_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data

View File

@@ -1,118 +0,0 @@
[
{
"account_category_name": "Cash and Cash Equivalents",
"description": "Cash on hand, demand deposits, and short-term highly liquid investments readily convertible to cash with original maturities of three months or less. Examples: Cash in hand, bank current accounts, money market funds, treasury bills \u22643 months."
},
{
"account_category_name": "Cost of Goods Sold",
"description": "Direct costs attributable to cost of goods sold. Examples: Raw materials, stock in trade."
},
{
"account_category_name": "Current Tax Liabilities",
"description": "Income tax obligations for current and prior periods. Examples: Provision for income tax, advance tax paid, tax deducted at source."
},
{
"account_category_name": "Finance Costs",
"description": "Interest and financing-related expenses. Examples: Interest on borrowings, bank charges, lease interest, foreign exchange losses."
},
{
"account_category_name": "Intangible Assets",
"description": "Identifiable non-monetary assets without physical substance. Examples: Software, patents, trademarks, licenses, development costs."
},
{
"account_category_name": "Investment Income",
"description": "Returns generated from financial investments and cash management. Examples: Interest income, dividend income, rental income, fair value gains."
},
{
"account_category_name": "Long-term Borrowings",
"description": "Interest-bearing debt obligations with maturity beyond one year. Examples: Term loans, bonds, debentures, mortgages."
},
{
"account_category_name": "Long-term Investments",
"description": "Investments held for strategic purposes or extended periods. Examples: Equity investments, bonds, associates, joint ventures, deposits."
},
{
"account_category_name": "Long-term Provisions",
"description": "Present obligations beyond one year with uncertain timing/amount. Examples: Asset retirement obligations, environmental remediation, legal settlements."
},
{
"account_category_name": "Operating Expenses",
"description": "Costs incurred in ordinary business operations excluding direct costs. Examples: Selling expenses, administrative costs, marketing, utilities, rent."
},
{
"account_category_name": "Other Current Assets",
"description": "Current assets not classified elsewhere including prepaid expenses and advances. Examples: Prepaid insurance, prepaid rent, advance to suppliers, security deposits recoverable within one year."
},
{
"account_category_name": "Other Current Liabilities",
"description": "Short-term obligations not classified elsewhere. Examples: Accrued expenses, statutory liabilities, employee payables."
},
{
"account_category_name": "Other Direct Costs",
"description": "Direct costs excluding cost of goods sold. Examples: Direct labor, manufacturing overhead, freight inward."
},
{
"account_category_name": "Other Non-current Assets",
"description": "Long-term assets not classified elsewhere. Examples: Security deposits, long-term prepayments, advances for capital goods."
},
{
"account_category_name": "Other Non-current Liabilities",
"description": "Long-term obligations not classified elsewhere. Examples: Long-term deposits, deferred income, government grants."
},
{
"account_category_name": "Other Operating Income",
"description": "Incidental income related to business operations but not core revenue. Examples: Scrap sales, government grants, insurance claims, foreign exchange gains."
},
{
"account_category_name": "Other Payables",
"description": "Non-trade payables and obligations to parties other than suppliers. Examples: Employee payables, accrued expenses, customer advances, security deposits received."
},
{
"account_category_name": "Other Receivables",
"description": "Non-trade amounts due to the entity excluding financing arrangements. Examples: Employee advances, insurance claims, tax refunds, deposits recoverable."
},
{
"account_category_name": "Reserves and Surplus",
"description": "Accumulated profits and other reserves created from profits or share premium. Examples: General reserves, retained earnings, statutory reserves, share premium."
},
{
"account_category_name": "Revenue from Operations",
"description": "Income from primary business activities in ordinary course. Examples: Sales of goods, service revenue, commission income, royalty income."
},
{
"account_category_name": "Share Capital",
"description": "Nominal value of issued and paid-up equity shares. Examples: Common stock, ordinary shares, preference shares."
},
{
"account_category_name": "Short-term Borrowings",
"description": "Interest-bearing debt obligations due within one year. Examples: Bank overdrafts, short-term loans, current portion of long-term debt."
},
{
"account_category_name": "Short-term Investments",
"description": "Financial instruments held for short-term investment purposes, readily convertible to cash. Examples: Marketable securities, fixed deposits >3 months, mutual funds."
},
{
"account_category_name": "Short-term Provisions",
"description": "Present obligations due within one year with uncertain timing or amount. Examples: Warranty provisions, legal claims, restructuring costs."
},
{
"account_category_name": "Stock Assets",
"description": "Inventory and stock-related assets including raw materials, work in progress, finished goods, and stock in trade. Examples: Raw materials, finished goods, trading merchandise, consumables."
},
{
"account_category_name": "Tangible Assets",
"description": "Physical assets used in business operations including property, plant, and equipment. Examples: Land, buildings, machinery, equipment, vehicles, furniture, capital work in progress."
},
{
"account_category_name": "Tax Expense",
"description": "Current and deferred income tax obligations. Examples: Current tax provision, deferred tax expense, withholding taxes."
},
{
"account_category_name": "Trade Payables",
"description": "Amounts owed to suppliers. Examples: Supplier invoices, accrued purchases, bills payable."
},
{
"account_category_name": "Trade Receivables",
"description": "Amounts due from customers for goods sold or services provided in ordinary course of business. Examples: Accounts receivable, notes receivable from customers, unbilled revenue."
}
]

View File

@@ -1,993 +0,0 @@
{
"creation": "2025-09-07 07:24:40.762641",
"disabled": 0,
"docstatus": 0,
"doctype": "Financial Report Template",
"idx": 0,
"modified": "2025-10-15 03:12:19.165699",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Horizontal Balance Sheet (Columnar)",
"owner": "Administrator",
"report_type": "Balance Sheet",
"rows": [
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Section Break",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Column Break",
"display_name": "Equity & Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "Capital & Reserves",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CAPITAL_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Share Capital\"]",
"data_source": "Account Data",
"display_name": "Share Capital",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_SHARE_CAPITAL",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "{\"and\":[[\"account_category\",\"=\",\"Reserves and Surplus\"],[\"account_name\",\"not like\",\"%Retained Earnings%\"]]}",
"data_source": "Account Data",
"display_name": "Reserves & Surplus",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_RESERVES",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "{\"and\":[[\"account_name\",\"like\",\"%Retained Earnings%\"],[\"account_category\",\"=\",\"Reserves and Surplus\"]]}",
"data_source": "Account Data",
"display_name": "Retained Earnings",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_RETAINED_EARNINGS",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "L_SHARE_CAPITAL + L_RESERVES + L_RETAINED_EARNINGS",
"data_source": "Calculated Amount",
"display_name": "Total Capital & Reserves",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "L_TOTAL_EQUITY",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "Non-Current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "NCL_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Long-term Borrowings\"], [\"account_name\", \"not like\", \"Unsecured\"]]}",
"data_source": "Account Data",
"display_name": "Secured Loans",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_SECURED_LOANS",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Long-term Borrowings\"], [\"account_name\", \"like\", \"Unsecured\"]]}",
"data_source": "Account Data",
"display_name": "Unsecured Loans",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_UNSECURED_LOANS",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Long-term Provisions\"]",
"data_source": "Account Data",
"display_name": "Long-term Provisions",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_LT_PROVISIONS",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Non-current Liabilities\"]",
"data_source": "Account Data",
"display_name": "Other Non-current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_OTHER_NCL",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "L_SECURED_LOANS + L_UNSECURED_LOANS + L_LT_PROVISIONS + L_OTHER_NCL",
"data_source": "Calculated Amount",
"display_name": "Total Non-current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "L_TOTAL_NCL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "Current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CL_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Short-term Borrowings\"]",
"data_source": "Account Data",
"display_name": "Short-term Borrowings",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_ST_BORROWINGS",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Trade Payables\"]",
"data_source": "Account Data",
"display_name": "Trade Payables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_TRADE_PAYABLES",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Payables\"]",
"data_source": "Account Data",
"display_name": "Other Payables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_OTHER_PAYABLES",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Current Tax Liabilities\"]",
"data_source": "Account Data",
"display_name": "Current Tax Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_TAX_LIABILITIES",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Short-term Provisions\"]",
"data_source": "Account Data",
"display_name": "Short-term Provisions",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_PROVISIONS",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Current Liabilities\"]",
"data_source": "Account Data",
"display_name": "Other Current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_OTHER_CL",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "L_ST_BORROWINGS + L_TRADE_PAYABLES + L_OTHER_PAYABLES + L_TAX_LIABILITIES + L_PROVISIONS + L_OTHER_CL",
"data_source": "Calculated Amount",
"display_name": "Total Current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "L_TOTAL_CL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "Current Year Earnings",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "PL_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 1,
"calculation_formula": "[\"root_type\", \"in\", [\"Income\", \"Expense\"]]",
"color": "#28a745",
"data_source": "Account Data",
"display_name": "Net Profit/(Loss) for the Year",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "L_CURRENT_PL",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Column Break",
"display_name": "Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "Non-Current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "NCA_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Tangible Assets\"], [\"account_type\", \"!=\", \"Accumulated Depreciation\"]]}",
"data_source": "Account Data",
"display_name": "Property, Plant & Equipment",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_TANGIBLE",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_type\", \"like\", \"Accumulated Depreciation\"]",
"data_source": "Account Data",
"display_name": "Less: Accumulated Depreciation",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 2,
"italic_text": 1,
"reference_code": "A_ACC_DEPRECIATION",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "A_TANGIBLE - A_ACC_DEPRECIATION",
"data_source": "Calculated Amount",
"display_name": "Net Tangible Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_NET_TANGIBLE",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Intangible Assets\"]",
"data_source": "Account Data",
"display_name": "Intangible Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_INTANGIBLE",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Long-term Investments\"]",
"data_source": "Account Data",
"display_name": "Long-term Investments",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_LT_INVESTMENTS",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Non-current Assets\"]",
"data_source": "Account Data",
"display_name": "Other Non-current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_OTHER_NCA",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "(A_NET_TANGIBLE if A_ACC_DEPRECIATION else A_TANGIBLE) + A_INTANGIBLE + A_LT_INVESTMENTS + A_OTHER_NCA",
"data_source": "Calculated Amount",
"display_name": "Total Non-current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "A_TOTAL_NCA",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "Current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CA_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Stock Assets\"]",
"data_source": "Account Data",
"display_name": "Inventories",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_STOCK",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Trade Receivables\"]",
"data_source": "Account Data",
"display_name": "Trade Receivables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_TRADE_RECEIVABLES",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Receivables\"]",
"data_source": "Account Data",
"display_name": "Other Receivables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_OTHER_RECEIVABLES",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Short-term Investments\"]",
"data_source": "Account Data",
"display_name": "Short-term Investments",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_ST_INVESTMENTS",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Cash and Cash Equivalents\"]",
"data_source": "Account Data",
"display_name": "Cash & Bank Balances",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_CASH_BANK",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Current Assets\"]",
"data_source": "Account Data",
"display_name": "Other Current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "A_OTHER_CA",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "A_STOCK + A_TRADE_RECEIVABLES + A_OTHER_RECEIVABLES + A_ST_INVESTMENTS + A_CASH_BANK + A_OTHER_CA",
"data_source": "Calculated Amount",
"display_name": "Total Current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "A_TOTAL_CA",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Section Break",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "L_TOTAL_EQUITY + L_TOTAL_NCL + L_TOTAL_CL + L_CURRENT_PL",
"data_source": "Calculated Amount",
"display_name": "TOTAL",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "L_GRAND_TOTAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Column Break",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "A_TOTAL_NCA + A_TOTAL_CA",
"data_source": "Calculated Amount",
"display_name": "TOTAL",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "A_GRAND_TOTAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Section Break",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "L_GRAND_TOTAL - A_GRAND_TOTAL",
"color": "#EC864B",
"data_source": "Calculated Amount",
"display_name": "Balance Check (should be zero)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 1,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "BALANCE_CHECK",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "A_TOTAL_NCA + A_TOTAL_CA",
"data_source": "Calculated Amount",
"display_name": "Total Assets",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TOTAL_ASSETS",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "L_TOTAL_NCL + L_TOTAL_CL",
"data_source": "Calculated Amount",
"display_name": "Total Liabilities",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TOTAL_LIABILITIES",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "L_TOTAL_EQUITY + L_CURRENT_PL",
"data_source": "Calculated Amount",
"display_name": "Total Equity",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TOTAL_EQUITY",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "KEY FINANCIAL RATIOS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "METRICS_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(A_TOTAL_CA / L_TOTAL_CL) if L_TOTAL_CL != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Current Ratio",
"fieldtype": "Float",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "CURRENT_RATIO",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "((A_TRADE_RECEIVABLES + A_ST_INVESTMENTS + A_CASH_BANK) / L_TOTAL_CL) if L_TOTAL_CL != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Quick Ratio (Acid Test)",
"fieldtype": "Float",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "QUICK_RATIO",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "((L_SECURED_LOANS + L_UNSECURED_LOANS + L_ST_BORROWINGS) / TOTAL_EQUITY) if TOTAL_EQUITY != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Debt to Equity Ratio",
"fieldtype": "Float",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "DEBT_EQUITY_RATIO",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "A_TOTAL_CA - L_TOTAL_CL",
"data_source": "Calculated Amount",
"display_name": "Net Working Capital",
"fieldtype": "Currency",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "WORKING_CAPITAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(TOTAL_EQUITY / TOTAL_ASSETS * 100) if TOTAL_ASSETS != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Equity Ratio %",
"fieldtype": "Percent",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "EQUITY_RATIO",
"reverse_sign": 0
}
],
"template_name": "Horizontal Balance Sheet (Columnar)"
}

View File

@@ -1,824 +0,0 @@
{
"creation": "2025-09-06 21:47:56.970556",
"disabled": 0,
"docstatus": 0,
"doctype": "Financial Report Template",
"idx": 0,
"modified": "2025-10-15 03:13:38.485684",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Standard Balance Sheet (IFRS)",
"owner": "Administrator",
"report_type": "Balance Sheet",
"rows": [
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "ASSETS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "ASSETS_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "CURRENT ASSETS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CA_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Cash and Cash Equivalents\"]",
"data_source": "Account Data",
"display_name": "Cash and Cash Equivalents",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CA100",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Trade Receivables\"]",
"data_source": "Account Data",
"display_name": "Trade Receivables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CA200",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Receivables\"]",
"data_source": "Account Data",
"display_name": "Other Receivables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CA300",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Stock Assets\"]",
"data_source": "Account Data",
"display_name": "Inventories",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CA400",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Short-term Investments\"]",
"data_source": "Account Data",
"display_name": "Short-term Investments",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CA500",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Current Assets\"]",
"data_source": "Account Data",
"display_name": "Other Current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CA600",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CA100 + CA200 + CA300 + CA400 + CA500 + CA600",
"data_source": "Calculated Amount",
"display_name": "Total Current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "CA_TOTAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "NON-CURRENT ASSETS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "NCA_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Tangible Assets\"]",
"data_source": "Account Data",
"display_name": "Property, Plant and Equipment",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCA100",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Intangible Assets\"]",
"data_source": "Account Data",
"display_name": "Intangible Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCA200",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Long-term Investments\"]",
"data_source": "Account Data",
"display_name": "Long-term Investments",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCA300",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Non-current Assets\"]",
"data_source": "Account Data",
"display_name": "Other Non-current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCA400",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "NCA100 + NCA200 + NCA300 + NCA400",
"data_source": "Calculated Amount",
"display_name": "Total Non-current Assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "NCA_TOTAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CA_TOTAL + NCA_TOTAL",
"data_source": "Calculated Amount",
"display_name": "TOTAL ASSETS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TOTAL_ASSETS",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "LIABILITIES AND EQUITY",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "LIAB_EQUITY_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "CURRENT LIABILITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CL_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Trade Payables\"]",
"data_source": "Account Data",
"display_name": "Trade Payables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CL100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Payables\"]",
"data_source": "Account Data",
"display_name": "Other Payables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CL200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Short-term Borrowings\"]",
"data_source": "Account Data",
"display_name": "Short-term Borrowings",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CL300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Current Tax Liabilities\"]",
"data_source": "Account Data",
"display_name": "Current Tax Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CL400",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Short-term Provisions\"]",
"data_source": "Account Data",
"display_name": "Short-term Provisions",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CL500",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Current Liabilities\"]",
"data_source": "Account Data",
"display_name": "Other Current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CL600",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CL100 + CL200 + CL300 + CL400 + CL500 + CL600",
"data_source": "Calculated Amount",
"display_name": "Total Current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "CL_TOTAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "NON-CURRENT LIABILITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "NCL_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Long-term Borrowings\"]",
"data_source": "Account Data",
"display_name": "Long-term Borrowings",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCL100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Long-term Provisions\"]",
"data_source": "Account Data",
"display_name": "Long-term Provisions",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCL200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Non-current Liabilities\"]",
"data_source": "Account Data",
"display_name": "Other Non-current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "NCL300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "NCL100 + NCL200 + NCL300",
"data_source": "Calculated Amount",
"display_name": "Total Non-current Liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "NCL_TOTAL",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CL_TOTAL + NCL_TOTAL",
"data_source": "Calculated Amount",
"display_name": "TOTAL LIABILITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TOTAL_LIABILITIES",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"data_source": "Blank Line",
"display_name": "EQUITY",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "EQ_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Share Capital\"]",
"data_source": "Account Data",
"display_name": "Share Capital",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "EQ100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Reserves and Surplus\"]",
"data_source": "Account Data",
"display_name": "Reserves and Surplus",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "EQ200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 0,
"calculation_formula": "[\"root_type\", \"in\", [\"Income\", \"Expense\"]]",
"data_source": "Account Data",
"display_name": "Provisional Profit and Loss",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 1,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "EQ300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "EQ100 + EQ200 + EQ300",
"data_source": "Calculated Amount",
"display_name": "Total Equity",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "TOTAL_EQUITY",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "TOTAL_EQUITY",
"data_source": "Calculated Amount",
"display_name": "TOTAL EQUITY",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "TOTAL_LIABILITIES + TOTAL_EQUITY",
"data_source": "Calculated Amount",
"display_name": "TOTAL LIABILITIES AND EQUITY",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TOTAL_LIAB_EQUITY",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "TOTAL_ASSETS - TOTAL_LIAB_EQUITY",
"color": "#CB2929",
"data_source": "Calculated Amount",
"display_name": "Balance Check (should be zero)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 1,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "BALANCE_CHECK",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "CA_TOTAL / CL_TOTAL if CL_TOTAL != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Current Ratio",
"fieldtype": "Float",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CURRENT_RATIO",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(CL300 + NCL100) / TOTAL_EQUITY if TOTAL_EQUITY != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Debt to Equity Ratio",
"fieldtype": "Float",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "DEBT_EQUITY_RATIO",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "CA_TOTAL - CL_TOTAL",
"data_source": "Calculated Amount",
"display_name": "Working Capital",
"fieldtype": "Currency",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "WORKING_CAPITAL",
"reverse_sign": 0
}
],
"template_name": "Standard Balance Sheet (IFRS)"
}

View File

@@ -1,832 +0,0 @@
{
"creation": "2025-09-07 22:45:05.754628",
"disabled": 0,
"docstatus": 0,
"doctype": "Financial Report Template",
"idx": 0,
"modified": "2025-10-27 08:25:12.870928",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Standard Cash Flow Statement (IFRS)",
"owner": "Administrator",
"report_type": "Cash Flow",
"rows": [
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "CASH FLOWS FROM OPERATING ACTIVITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_OP_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"root_type\", \"in\", [\"Income\", \"Expense\"]], [\"account_category\", \"!=\", \"Tax Expense\"]]}",
"color": "",
"data_source": "Account Data",
"display_name": "Profit before tax",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_OP100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "Adjustments for:",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CF_ADJ_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_type\", \"=\", \"Depreciation\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Depreciation and amortization",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_ADJ100",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Finance Costs\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Finance costs",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_ADJ200",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Investment Income\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Investment income",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_ADJ300",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_OP100 + CF_ADJ100 + CF_ADJ200 + CF_ADJ300",
"color": "",
"data_source": "Calculated Amount",
"display_name": "Operating profit before working capital changes",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CF_OP_BEFORE_WC",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "Working capital changes:",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CF_WC_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Trade Receivables\"]",
"color": "",
"data_source": "Account Data",
"display_name": "(Increase)/decrease in trade receivables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_WC100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Stock Assets\"]",
"color": "",
"data_source": "Account Data",
"display_name": "(Increase)/decrease in inventories",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_WC200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"in\", [\"Other Receivables\", \"Other Current Assets\"]]",
"color": "",
"data_source": "Account Data",
"display_name": "(Increase)/decrease in other current assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_WC300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Trade Payables\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Increase/(decrease) in trade payables",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_WC400",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"in\", [\"Other Payables\", \"Current Tax Liabilities\", \"Short-term Borrowings\", \"Short-term Provisions\", \"Other Current Liabilities\"]]",
"color": "",
"data_source": "Account Data",
"display_name": "Increase/(decrease) in other current liabilities",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 0,
"reference_code": "CF_WC500",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_OP_BEFORE_WC + CF_WC100 + CF_WC200 + CF_WC300 + CF_WC400 + CF_WC500",
"color": "",
"data_source": "Calculated Amount",
"display_name": "Cash generated from operations",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_CASH_GEN",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Finance Costs\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Interest paid",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_OP200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Tax Expense\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Income taxes paid",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_OP300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_CASH_GEN + CF_OP200 + CF_OP300",
"color": "",
"data_source": "Calculated Amount",
"display_name": "NET CASH FROM OPERATING ACTIVITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_OP_NET",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "CASH FLOWS FROM INVESTING ACTIVITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Tangible Assets\"], [\"account_type\", \"!=\", \"Accumulated Depreciation\"]]}",
"color": "",
"data_source": "Account Data",
"display_name": "Purchase / Sale of property, plant and equipment",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_type\", \"=\", \"Accumulated Depreciation\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Accumulated Depreciation",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV101",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "max(0, CF_ADJ100 - CF_INV101)",
"color": "",
"data_source": "Calculated Amount",
"display_name": "Depreciation and amortization",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 1,
"include_in_charts": 0,
"indentation_level": 1,
"italic_text": 1,
"reference_code": "CF_INV102",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Intangible Assets\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Purchase of intangible assets",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"in\", [\"Long-term Investments\", \"Short-term Investments\"]]",
"color": "",
"data_source": "Account Data",
"display_name": "Purchase of investments",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Investment Income\"], [\"account_name\", \"not like\", \"dividend\"]]}",
"color": "",
"data_source": "Account Data",
"display_name": "Interest received",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV400",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Investment Income\"], [\"account_name\", \"like\", \"dividend\"]]}",
"color": "",
"data_source": "Account Data",
"display_name": "Dividends received",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV500",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_INV100 + CF_INV102 + CF_INV200 + CF_INV300 + CF_INV400 + CF_INV500",
"color": "",
"data_source": "Calculated Amount",
"display_name": "NET CASH FROM INVESTING ACTIVITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_INV_NET",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "CASH FLOWS FROM FINANCING ACTIVITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_FIN_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"in\", [\"Share Capital\", \"Reserves and Surplus\"]], [\"account_name\", \"not like\", \"Dividends Paid\"]]}",
"color": "",
"data_source": "Account Data",
"display_name": "Proceeds from issue of share capital",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_FIN100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"in\", [\"Long-term Borrowings\", \"Short-term Borrowings\"]]",
"color": "",
"data_source": "Account Data",
"display_name": "Proceeds from / Repayment of borrowings",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_FIN200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"in\", [\"Share Capital\", \"Reserves and Surplus\"]], [\"account_name\", \"like\", \"Dividends Paid\"]]}",
"color": "",
"data_source": "Account Data",
"display_name": "Dividends paid",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_FIN300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_FIN100 + CF_FIN200 + CF_FIN300",
"color": "",
"data_source": "Calculated Amount",
"display_name": "NET CASH FROM FINANCING ACTIVITIES",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_FIN_NET",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_OP_NET + CF_INV_NET + CF_FIN_NET",
"color": "",
"data_source": "Calculated Amount",
"display_name": "NET INCREASE/(DECREASE) IN CASH AND CASH EQUIVALENTS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_NET_INCREASE",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Opening Balance",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Cash and Cash Equivalents\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Cash and cash equivalents at beginning of period",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_CASH_BEGIN",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_type\", \"like\", \"Exchange\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Effect of exchange rate changes",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_FX_EFFECT",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Closing Balance",
"bold_text": 1,
"calculation_formula": "[\"account_category\", \"=\", \"Cash and Cash Equivalents\"]",
"color": "",
"data_source": "Account Data",
"display_name": "Cash and cash equivalents at end of period",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_CASH_END",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "CF_CASH_END - (CF_CASH_BEGIN + CF_NET_INCREASE + CF_FX_EFFECT)",
"color": "#CB2929",
"data_source": "Calculated Amount",
"display_name": "Validation Check (should be zero)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 1,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "CF_VALIDATION",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "",
"color": "",
"data_source": "Blank Line",
"display_name": "CASH FLOW METRICS",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CF_METRICS_HEADER",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "CF_OP_NET + CF_INV100",
"color": "",
"data_source": "Calculated Amount",
"display_name": "Free Cash Flow",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CF_FREE_CASH",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(CF_OP_NET / (CF_FIN300 + CF_OP200)) if (CF_FIN300 + CF_OP200) != 0 else 0",
"color": "",
"data_source": "Calculated Amount",
"display_name": "Cash Flow Coverage Ratio",
"fieldtype": "Float",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "CF_COVERAGE",
"reverse_sign": 0
}
],
"template_name": "Standard Cash Flow Statement (IFRS)"
}

View File

@@ -1,418 +0,0 @@
{
"creation": "2025-09-06 10:23:05.259864",
"disabled": 0,
"docstatus": 0,
"doctype": "Financial Report Template",
"idx": 0,
"modified": "2025-09-15 15:02:15.911105",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Standard Profit and Loss (IFRS)",
"owner": "Administrator",
"report_type": "Profit and Loss Statement",
"rows": [
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\":[[\"account_category\",\"=\",\"Revenue from Operations\"]]}",
"data_source": "Account Data",
"display_name": "Sales Revenue",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "REV100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Cost of Goods Sold\"]",
"data_source": "Account Data",
"display_name": "Cost of Goods Sold",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "COGS100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Direct Costs\"]",
"data_source": "Account Data",
"display_name": "Other Direct Costs",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "COGS200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "REV100 + COGS100 + COGS200",
"data_source": "Calculated Amount",
"display_name": "GROSS PROFIT (GP)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "GROSS_PROFIT",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Other Operating Income\"]",
"data_source": "Account Data",
"display_name": "Other Operating Income",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "OPIN100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Operating Expenses\"], [\"account_name\", \"like\", \"Sales\"]]}",
"data_source": "Account Data",
"display_name": "Selling & Distribution Expenses",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "OPEX100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Operating Expenses\"], [\"account_name\", \"like\", \"Administrative\"]]}",
"data_source": "Account Data",
"display_name": "Administrative Expenses",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "OPEX200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "{\"and\": [[\"account_category\", \"=\", \"Operating Expenses\"], [\"account_name\", \"not like\", \"Sales\"], [\"account_name\", \"not like\", \"Administrative\"]]}",
"data_source": "Account Data",
"display_name": "Other Operating Expenses",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "OPEX300",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "GROSS_PROFIT + OPIN100 + OPEX100 + OPEX200 + OPEX300",
"data_source": "Calculated Amount",
"display_name": "OPERATING PROFIT (EBIT)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "EBIT",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Investment Income\"]",
"data_source": "Account Data",
"display_name": "Investment Income",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "FIN100",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Finance Costs\"]",
"data_source": "Account Data",
"display_name": "Finance Costs",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "FIN200",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "EBIT + FIN100 + FIN200",
"data_source": "Calculated Amount",
"display_name": "PROFIT BEFORE TAX (EBT)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "PBT",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"account_category\", \"=\", \"Tax Expense\"]",
"data_source": "Account Data",
"display_name": "Tax Expense",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "TAX",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "PBT + TAX",
"data_source": "Calculated Amount",
"display_name": "NET PROFIT (NP)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "NET_PROFIT",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "NET_PROFIT - ACT_NET_PROFIT",
"color": "#CB2929",
"data_source": "Calculated Amount",
"display_name": "VARIANCE (Calculated vs Actual)",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 1,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "VAL_DIFF",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"data_source": "Blank Line",
"fieldtype": "",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 0,
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(GROSS_PROFIT / REV100) * 100 if REV100 != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Gross Profit Margin %",
"fieldtype": "Percent",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "GP_MARGIN",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(EBIT / REV100) * 100 if REV100 != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Operating Profit Margin %",
"fieldtype": "Percent",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "OP_MARGIN",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 0,
"calculation_formula": "(NET_PROFIT / REV100) * 100 if REV100 != 0 else 0",
"data_source": "Calculated Amount",
"display_name": "Net Profit Margin %",
"fieldtype": "Percent",
"hidden_calculation": 0,
"hide_when_empty": 0,
"include_in_charts": 0,
"indentation_level": 0,
"italic_text": 1,
"reference_code": "NP_MARGIN",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"root_type\", \"=\", \"Income\"]",
"data_source": "Account Data",
"display_name": "Total Income",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "ACT_TOTAL_INCOME",
"reverse_sign": 1
},
{
"advanced_filtering": 0,
"balance_type": "Period Movement (Debits - Credits)",
"bold_text": 0,
"calculation_formula": "[\"root_type\", \"=\", \"Expense\"]",
"data_source": "Account Data",
"display_name": "Total Expenses",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "ACT_TOTAL_EXPENSES",
"reverse_sign": 0
},
{
"advanced_filtering": 0,
"balance_type": "",
"bold_text": 1,
"calculation_formula": "ACT_TOTAL_INCOME - ACT_TOTAL_EXPENSES",
"data_source": "Calculated Amount",
"display_name": "Net Profit",
"fieldtype": "",
"hidden_calculation": 1,
"hide_when_empty": 0,
"include_in_charts": 1,
"indentation_level": 0,
"italic_text": 0,
"reference_code": "ACT_NET_PROFIT",
"reverse_sign": 0
}
],
"template_name": "Standard Profit and Loss (IFRS)"
}

View File

@@ -660,7 +660,6 @@ def make_reverse_gl_entries(
adv_adj=False,
update_outstanding="Yes",
partial_cancel=False,
posting_date=None,
):
"""
Get original gl entries of the voucher
@@ -758,8 +757,6 @@ def make_reverse_gl_entries(
if immutable_ledger_enabled:
new_gle["is_cancelled"] = 0
new_gle["posting_date"] = frappe.form_dict.get("posting_date") or getdate()
elif posting_date:
new_gle["posting_date"] = posting_date
if new_gle["debit"] or new_gle["credit"]:
make_entry(new_gle, adv_adj, "Yes")

View File

@@ -39,16 +39,6 @@ class TestAccountBalance(IntegrationTestCase):
"currency": "EUR",
"balance": 0.0,
},
{
"account": "Interest Income - _TC2",
"currency": "EUR",
"balance": 0.0,
},
{
"account": "Interest on Fixed Deposits - _TC2",
"currency": "EUR",
"balance": 0.0,
},
{
"account": "Sales - _TC2",
"currency": "EUR",

View File

@@ -26,13 +26,16 @@ frappe.query_reports["Accounts Payable"] = {
{
fieldname: "cost_center",
label: __("Cost Center"),
fieldtype: "MultiSelectList",
get_data: function (txt) {
return frappe.db.get_link_options("Cost Center", txt, {
company: frappe.query_report.get_filter_value("company"),
});
},
fieldtype: "Link",
options: "Cost Center",
get_query: () => {
var company = frappe.query_report.get_filter_value("company");
return {
filters: {
company: company,
},
};
},
},
{
fieldname: "party_account",

Some files were not shown because too many files have changed in this diff Show More