Cutomisable Cash Flow Reports (#12969)
* add child doctype - Cash Flow Mapping Account
* adds new doctype - Cash Flow Mapping
* adds new doctype - Cash Flow Mapper
* adds new doctype Cash Flow Mapping Template
* adds new doctype Cash Flow Mapping Template
* adds adjustments to Cash Flow Mapper:
- remove fields from Cash Flow Mapping Template Details
- update in Cash FLow Mapper
* get cash_flow_accouts from Cash Flow Mapping
* change `tmp` to `mappers` and make sure `mappers` is sorted by its `position` field
* changes description from 'Net Profit/Loss' to 'Profit for the year'
* set `net_profit_loss` `parent_account` properly
* modify `get_account_type_based_data`:
- changed signature such that `account_type` parameter is now `account_name`
- where clause in query is now based on `name`
* remove zero rows
* de-duplicates row, summing similar accounts in the process
* makes gl sum calculation use `parent_account` as a condition
* add the `section_leader` immediately after adding net profit, sorts `accounts` by `is_working_capital` field
* adds `is_working_capital` to "account_types" so that we can use this to determine when to add "Changes in working capital" for operating activities
* add "Movement in working capital" subheader
* refactor code for readability
* adds new fields to `Cash Flow Mapping`:
- `is_interest_paid` to allow me recognise accounts for 'Interest Paid'
- `is_income_tax_paid` to allow me recognise accounts for 'Income Taxes Paid'
* allow `Cash Flow Mapping` to be renamable
* adds new field - `section_subtotal` useful for only Operating Activities
* changes `Cash Flow Mapping` doctype fields:
- remove `is_income_tax`_field
- add `is_income_tax_liability` field to identify tax payable accounts
- add `is_income_tax_expense` field to identify tax expense accounts in P or L
* calculates and shows tax paid adjustment in cash flow statement
* renames `is_interest_paid` to `is_finance_cost`
* - adds finance costs calculation
- correctly sets opening balance dates
* prevents users from selecting extra options in Cash Flow Mapping
* adds validation to prevent selecting multiple options
* adds new fields to Cash Flow Mapping
* calculate non cash p or l items (2nd pass)
* separates default cash flow generation from custom
* adds new setting to Accounts Settings:
- allow user elect to use customised cash flow report
* clean up
* removes mandatory constraint from accounts field
* allow rename, disallow create and delete
* adds patch to add default Cash Flow Mappers
* refactors custom_cashflow
* add article to explain configuration
* refactor
* further refactor
* final clean up (hopefully)
* clean up for codacy
* more codacy fixes
* more codacy fixes
* fix broken patch
* rename article to .md
* create default mappers after install
* PEP 8
* create the tables in `after_install` call
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index b19bb2b..e040b61 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -514,6 +514,68 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "report_settings_sb",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Report Settings",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "description": "Only select if you have setup Cash Flow Mapper documents",
+ "fieldname": "use_custom_cash_flow",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Use Custom Cash Flow Format",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"has_web_view": 0,
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/__init__.py b/erpnext/accounts/doctype/cash_flow_mapper/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/__init__.py
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js
new file mode 100644
index 0000000..13d223a
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js
@@ -0,0 +1,6 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Cash Flow Mapper', {
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.json b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.json
new file mode 100644
index 0000000..f0e984d
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.json
@@ -0,0 +1,275 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 1,
+ "autoname": "field:section_name",
+ "beta": 0,
+ "creation": "2018-02-08 10:00:14.066519",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Section Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_header",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Section Header",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "description": "e.g Adjustments for:",
+ "fieldname": "section_leader",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Section Leader",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_subtotal",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Section Subtotal",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_footer",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Section Footer",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "accounts",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Accounts",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cash Flow Mapping Template Details",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "position",
+ "fieldtype": "Int",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Position",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-02-15 18:28:55.034933",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Cash Flow Mapper",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 0,
+ "delete": 0,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "name",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
new file mode 100644
index 0000000..7251533
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class CashFlowMapper(Document):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
new file mode 100644
index 0000000..6e7b687
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
@@ -0,0 +1,25 @@
+DEFAULT_MAPPERS = [
+ {
+ 'doctype': 'Cash Flow Mapper',
+ 'section_footer': 'Net cash generated by operating activities',
+ 'section_header': 'Cash flows from operating activities',
+ 'section_leader': 'Adjustments for',
+ 'section_name': 'Operating Activities',
+ 'position': 0,
+ 'section_subtotal': 'Cash generated from operations',
+ },
+ {
+ 'doctype': 'Cash Flow Mapper',
+ 'position': 1,
+ 'section_footer': 'Net cash used in investing activities',
+ 'section_header': 'Cash flows from investing activities',
+ 'section_name': 'Investing Activities'
+ },
+ {
+ 'doctype': 'Cash Flow Mapper',
+ 'position': 2,
+ 'section_footer': 'Net cash used in financing activites',
+ 'section_header': 'Cash flows from financing activities',
+ 'section_name': 'Financing Activities',
+ }
+]
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js
new file mode 100644
index 0000000..12ca254
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Cash Flow Mapper", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Cash Flow Mapper
+ () => frappe.tests.make('Cash Flow Mapper', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.py
new file mode 100644
index 0000000..f055e56
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapper/test_cash_flow_mapper.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+
+class TestCashFlowMapper(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/__init__.py b/erpnext/accounts/doctype/cash_flow_mapping/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping/__init__.py
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js
new file mode 100644
index 0000000..00c7165
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js
@@ -0,0 +1,43 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Cash Flow Mapping', {
+ refresh: function(frm) {
+ frm.events.disable_unchecked_fields(frm);
+ },
+ reset_check_fields: function(frm) {
+ frm.fields.filter(field => field.df.fieldtype === 'Check')
+ .map(field => frm.set_df_property(field.df.fieldname, 'read_only', 0));
+ },
+ has_checked_field(frm) {
+ const val = frm.fields.filter(field => field.value === 1);
+ return val.length ? 1 : 0;
+ },
+ _disable_unchecked_fields: function(frm) {
+ // get value of clicked field
+ frm.fields.filter(field => field.value === 0)
+ .map(field => frm.set_df_property(field.df.fieldname, 'read_only', 1));
+ },
+ disable_unchecked_fields: function(frm) {
+ frm.events.reset_check_fields(frm);
+ const checked = frm.events.has_checked_field(frm);
+ if (checked) {
+ frm.events._disable_unchecked_fields(frm);
+ }
+ },
+ is_working_capital: function(frm) {
+ frm.events.disable_unchecked_fields(frm);
+ },
+ is_finance_cost: function(frm) {
+ frm.events.disable_unchecked_fields(frm);
+ },
+ is_income_tax_liability: function(frm) {
+ frm.events.disable_unchecked_fields(frm);
+ },
+ is_income_tax_expense: function(frm) {
+ frm.events.disable_unchecked_fields(frm);
+ },
+ is_finance_cost_adjustment: function(frm) {
+ frm.events.disable_unchecked_fields(frm);
+ }
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.json b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.json
new file mode 100644
index 0000000..bd7fd1c
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.json
@@ -0,0 +1,359 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 1,
+ "autoname": "field:mapping_name",
+ "beta": 0,
+ "creation": "2018-02-08 09:28:44.678364",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "mapping_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "label",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Label",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "accounts",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Accounts",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cash Flow Mapping Accounts",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sb_1",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Select Maximum Of 1",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_finance_cost",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Finance Cost",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_working_capital",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Working Capital",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_finance_cost_adjustment",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Finance Cost Adjustment",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_income_tax_liability",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Income Tax Liability",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "fieldname": "is_income_tax_expense",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Is Income Tax Expense",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-02-15 08:25:18.693533",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Cash Flow Mapping",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Administrator",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "name",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
new file mode 100644
index 0000000..28d84b4
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+
+class CashFlowMapping(Document):
+ def validate(self):
+ self.validate_checked_options()
+
+ def validate_checked_options(self):
+ checked_fields = [d for d in self.meta.fields if d.fieldtype == 'Check' and self.get(d.fieldname) == 1]
+ if len(checked_fields) > 1:
+ frappe.throw(
+ frappe._('You can only select a maximum of one option from the list of check boxes.'),
+ title='Error'
+ )
+
+
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js
new file mode 100644
index 0000000..1970ca8
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Cash Flow Mapping", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Cash Flow Mapping
+ () => frappe.tests.make('Cash Flow Mapping', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
new file mode 100644
index 0000000..499c820
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping/test_cash_flow_mapping.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+
+class TestCashFlowMapping(unittest.TestCase):
+ def setUp(self):
+ if frappe.db.exists("Cash Flow Mapping", "Test Mapping"):
+ frappe.delete_doc('Cash Flow Mappping', 'Test Mapping')
+
+ def tearDown(self):
+ frappe.delete_doc('Cash Flow Mapping', 'Test Mapping')
+
+ def test_multiple_selections_not_allowed(self):
+ doc = frappe.new_doc('Cash Flow Mapping')
+ doc.mapping_name = 'Test Mapping'
+ doc.label = 'Test label'
+ doc.append(
+ 'accounts',
+ {'account': 'Accounts Receivable - _TC'}
+ )
+ doc.is_working_capital = 1
+ doc.is_finance_cost = 1
+
+ self.assertRaises(frappe.ValidationError, doc.insert)
+
+ doc.is_finance_cost = 0
+ doc.insert()
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_accounts/__init__.py b/erpnext/accounts/doctype/cash_flow_mapping_accounts/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_accounts/__init__.py
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.json b/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.json
new file mode 100644
index 0000000..470c87c
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.json
@@ -0,0 +1,73 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:account",
+ "beta": 0,
+ "creation": "2018-02-08 09:25:34.353995",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "account",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Account",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2018-02-08 09:25:34.353995",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Cash Flow Mapping Accounts",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py b/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py
new file mode 100644
index 0000000..fc63b8f
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_accounts/cash_flow_mapping_accounts.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class CashFlowMappingAccounts(Document):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/__init__.py b/erpnext/accounts/doctype/cash_flow_mapping_template/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/__init__.py
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js
new file mode 100644
index 0000000..8611153
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js
@@ -0,0 +1,6 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Cash Flow Mapping Template', {
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.json b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.json
new file mode 100644
index 0000000..27e19dc
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.json
@@ -0,0 +1,123 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2018-02-08 10:20:18.316801",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "template_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Template Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "mapping",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Cash Flow Mapping",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cash Flow Mapping Template Details",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-02-08 10:20:18.316801",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Cash Flow Mapping Template",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py
new file mode 100644
index 0000000..6f77a39
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class CashFlowMappingTemplate(Document):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js
new file mode 100644
index 0000000..12546ce
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Cash Flow Mapping Template", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Cash Flow Mapping Template
+ () => frappe.tests.make('Cash Flow Mapping Template', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.py b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.py
new file mode 100644
index 0000000..d6b964b
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template/test_cash_flow_mapping_template.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+
+class TestCashFlowMappingTemplate(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/__init__.py b/erpnext/accounts/doctype/cash_flow_mapping_template_details/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/__init__.py
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js
new file mode 100644
index 0000000..2e5dce4
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js
@@ -0,0 +1,6 @@
+// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Cash Flow Mapping Template Details', {
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.json b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.json
new file mode 100644
index 0000000..22cf797
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.json
@@ -0,0 +1,94 @@
+{
+ "allow_copy": 0,
+ "allow_guest_to_view": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:mapping",
+ "beta": 0,
+ "creation": "2018-02-08 10:18:48.513608",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_bulk_edit": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "mapping",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Mapping",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Cash Flow Mapping",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "has_web_view": 0,
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2018-02-08 10:33:39.413930",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Cash Flow Mapping Template Details",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "show_name_in_global_search": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1,
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py
new file mode 100644
index 0000000..e10b638
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+from frappe.model.document import Document
+
+
+class CashFlowMappingTemplateDetails(Document):
+ pass
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js
new file mode 100644
index 0000000..eecabda
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.js
@@ -0,0 +1,23 @@
+/* eslint-disable */
+// rename this file from _test_[name] to test_[name] to activate
+// and remove above this line
+
+QUnit.test("test: Cash Flow Mapping Template Details", function (assert) {
+ let done = assert.async();
+
+ // number of asserts
+ assert.expect(1);
+
+ frappe.run_serially([
+ // insert a new Cash Flow Mapping Template Details
+ () => frappe.tests.make('Cash Flow Mapping Template Details', [
+ // values to be set
+ {key: 'value'}
+ ]),
+ () => {
+ assert.equal(cur_frm.doc.key, 'value');
+ },
+ () => done()
+ ]);
+
+});
diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py
new file mode 100644
index 0000000..db5683a
--- /dev/null
+++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/test_cash_flow_mapping_template_details.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import unittest
+
+
+class TestCashFlowMappingTemplateDetails(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index f55d192..c81db38 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -4,12 +4,17 @@
from __future__ import unicode_literals
import frappe
from frappe import _
+from frappe.utils import cint
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
from erpnext.accounts.utils import get_fiscal_year
def execute(filters=None):
+ if cint(frappe.db.get_single_value('Accounts Settings', 'use_custom_cash_flow')):
+ from erpnext.accounts.report.cash_flow.custom_cash_flow import execute as execute_custom
+ return execute_custom(filters=filters)
+
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
filters.periodicity, filters.accumulated_values, filters.company)
@@ -44,10 +49,7 @@
}
# combine all cash flow accounts for iteration
- cash_flow_accounts = []
- cash_flow_accounts.append(operation_accounts)
- cash_flow_accounts.append(investing_accounts)
- cash_flow_accounts.append(financing_accounts)
+ cash_flow_accounts = [operation_accounts, investing_accounts, financing_accounts]
# compute net profit / loss
income = get_data(filters.company, "Income", "Credit", period_list,
@@ -100,6 +102,7 @@
return columns, data
+
def get_account_type_based_data(company, account_type, period_list, accumulated_values):
data = {}
total = 0
@@ -127,6 +130,7 @@
data["total"] = total
return data
+
def get_start_date(period, accumulated_values, company):
start_date = period["year_start_date"]
if accumulated_values:
@@ -134,6 +138,7 @@
return start_date
+
def add_total_row_account(out, data, label, period_list, currency):
total_row = {
"account_name": "'" + _("{0}").format(label) + "'",
diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
new file mode 100644
index 0000000..160b200
--- /dev/null
+++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py
@@ -0,0 +1,443 @@
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import add_to_date
+from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
+from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
+
+
+def get_mapper_for(mappers, position):
+ mapper_list = filter(lambda x: x['position'] == position, mappers)
+ return mapper_list[0] if mapper_list else []
+
+
+def get_mappers_from_db():
+ return frappe.get_all(
+ 'Cash Flow Mapper',
+ fields=[
+ 'section_name', 'section_header', 'section_leader', 'section_subtotal',
+ 'section_footer', 'name', 'position'],
+ order_by='position'
+ )
+
+
+def get_accounts_in_mappers(mapping_names):
+ return frappe.db.sql(
+ 'select cfma.name, cfm.label, cfm.is_working_capital, cfm.is_income_tax_liability, '
+ 'cfm.is_income_tax_expense, cfm.is_finance_cost, cfm.is_finance_cost_adjustment '
+ 'from `tabCash Flow Mapping Accounts` cfma '
+ 'join `tabCash Flow Mapping` cfm on cfma.parent=cfm.name '
+ 'where cfma.parent in %s '
+ 'order by cfm.is_working_capital',
+ (mapping_names,)
+ )
+
+
+def setup_mappers(mappers):
+ cash_flow_accounts = []
+
+ for mapping in mappers:
+ mapping['account_types'] = []
+ mapping['tax_liabilities'] = []
+ mapping['tax_expenses'] = []
+ mapping['finance_costs'] = []
+ mapping['finance_costs_adjustments'] = []
+ doc = frappe.get_doc('Cash Flow Mapper', mapping['name'])
+ mapping_names = [item.name for item in doc.accounts]
+
+ if not mapping_names:
+ continue
+
+ accounts = get_accounts_in_mappers(mapping_names)
+
+ account_types = [
+ dict(
+ name=account[0], label=account[1], is_working_capital=account[2],
+ is_income_tax_liability=account[3], is_income_tax_expense=account[4]
+ ) for account in accounts if not account[3]]
+
+ finance_costs_adjustments = [
+ dict(
+ name=account[0], label=account[1], is_finance_cost=account[5],
+ is_finance_cost_adjustment=account[6]
+ ) for account in accounts if account[6]]
+
+ tax_liabilities = [
+ dict(
+ name=account[0], label=account[1], is_income_tax_liability=account[3],
+ is_income_tax_expense=account[4]
+ ) for account in accounts if account[3]]
+
+ tax_expenses = [
+ dict(
+ name=account[0], label=account[1], is_income_tax_liability=account[3],
+ is_income_tax_expense=account[4]
+ ) for account in accounts if account[4]]
+
+ finance_costs = [
+ dict(
+ name=account[0], label=account[1], is_finance_cost=account[5])
+ for account in accounts if account[5]]
+
+ account_types_labels = sorted(
+ set(
+ [(d['label'], d['is_working_capital'], d['is_income_tax_liability'], d['is_income_tax_expense'])
+ for d in account_types]
+ ),
+ key=lambda x: x[1]
+ )
+
+ fc_adjustment_labels = sorted(
+ set(
+ [(d['label'], d['is_finance_cost'], d['is_finance_cost_adjustment'])
+ for d in finance_costs_adjustments if d['is_finance_cost_adjustment']]
+ ),
+ key=lambda x: x[2]
+ )
+
+ unique_liability_labels = sorted(
+ set(
+ [(d['label'], d['is_income_tax_liability'], d['is_income_tax_expense'])
+ for d in tax_liabilities]
+ ),
+ key=lambda x: x[0]
+ )
+
+ unique_expense_labels = sorted(
+ set(
+ [(d['label'], d['is_income_tax_liability'], d['is_income_tax_expense'])
+ for d in tax_expenses]
+ ),
+ key=lambda x: x[0]
+ )
+
+ unique_finance_costs_labels = sorted(
+ set(
+ [(d['label'], d['is_finance_cost']) for d in finance_costs]
+ ),
+ key=lambda x: x[0]
+ )
+
+ for label in account_types_labels:
+ names = [d['name'] for d in account_types if d['label'] == label[0]]
+ m = dict(label=label[0], names=names, is_working_capital=label[1])
+ mapping['account_types'].append(m)
+
+ for label in fc_adjustment_labels:
+ names = [d['name'] for d in finance_costs_adjustments if d['label'] == label[0]]
+ m = dict(label=label[0], names=names)
+ mapping['finance_costs_adjustments'].append(m)
+
+ for label in unique_liability_labels:
+ names = [d['name'] for d in tax_liabilities if d['label'] == label[0]]
+ m = dict(label=label[0], names=names, tax_liability=label[1], tax_expense=label[2])
+ mapping['tax_liabilities'].append(m)
+
+ for label in unique_expense_labels:
+ names = [d['name'] for d in tax_expenses if d['label'] == label[0]]
+ m = dict(label=label[0], names=names, tax_liability=label[1], tax_expense=label[2])
+ mapping['tax_expenses'].append(m)
+
+ for label in unique_finance_costs_labels:
+ names = [d['name'] for d in finance_costs if d['label'] == label[0]]
+ m = dict(label=label[0], names=names, is_finance_cost=label[1])
+ mapping['finance_costs'].append(m)
+
+ cash_flow_accounts.append(mapping)
+
+ return cash_flow_accounts
+
+
+def add_data_for_operating_activities(
+ filters, company_currency, profit_data, period_list, light_mappers, mapper, data):
+ has_added_working_capital_header = False
+ section_data = []
+
+ data.append({
+ "account_name": mapper['section_header'],
+ "parent_account": None,
+ "indent": 0.0,
+ "account": mapper['section_header']
+ })
+
+ if profit_data:
+ profit_data.update({
+ "indent": 1,
+ "parent_account": get_mapper_for(light_mappers, position=0)['section_header']
+ })
+ data.append(profit_data)
+ section_data.append(profit_data)
+
+ data.append({
+ "account_name": mapper["section_leader"],
+ "parent_account": None,
+ "indent": 1.0,
+ "account": mapper["section_leader"]
+ })
+
+ for account in mapper['account_types']:
+ if account['is_working_capital'] and not has_added_working_capital_header:
+ data.append({
+ "account_name": 'Movement in working capital',
+ "parent_account": None,
+ "indent": 1.0,
+ "account": ""
+ })
+ has_added_working_capital_header = True
+
+ account_data = _get_account_type_based_data(
+ filters, account['names'], period_list, filters.accumulated_values)
+
+ if not account['is_working_capital']:
+ for key in account_data:
+ if key != 'total':
+ account_data[key] *= -1
+
+ if account_data['total'] != 0:
+ account_data.update({
+ "account_name": account['label'],
+ "account": account['names'],
+ "indent": 1.0,
+ "parent_account": mapper['section_header'],
+ "currency": company_currency
+ })
+ data.append(account_data)
+ section_data.append(account_data)
+
+ _add_total_row_account(
+ data, section_data, mapper['section_subtotal'], period_list, company_currency, indent=1)
+
+ # calculate adjustment for tax paid and add to data
+ if not mapper['tax_liabilities']:
+ mapper['tax_liabilities'] = [
+ dict(label='Income tax paid', names=[''], tax_liability=1, tax_expense=0)]
+
+ for account in mapper['tax_liabilities']:
+ tax_paid = calculate_adjustment(
+ filters, mapper['tax_liabilities'], mapper['tax_expenses'],
+ filters.accumulated_values, period_list)
+
+ if tax_paid:
+ tax_paid.update({
+ 'parent_account': mapper['section_header'],
+ 'currency': company_currency,
+ 'account_name': account['label'],
+ 'indent': 1.0
+ })
+ data.append(tax_paid)
+ section_data.append(tax_paid)
+
+ if not mapper['finance_costs_adjustments']:
+ mapper['finance_costs_adjustments'] = [dict(label='Interest Paid', names=[''])]
+
+ for account in mapper['finance_costs_adjustments']:
+ interest_paid = calculate_adjustment(
+ filters, mapper['finance_costs_adjustments'], mapper['finance_costs'],
+ filters.accumulated_values, period_list
+ )
+
+ if interest_paid:
+ interest_paid.update({
+ 'parent_account': mapper['section_header'],
+ 'currency': company_currency,
+ 'account_name': account['label'],
+ 'indent': 1.0
+ })
+ data.append(interest_paid)
+ section_data.append(interest_paid)
+
+ _add_total_row_account(
+ data, section_data, mapper['section_footer'], period_list, company_currency)
+
+
+def calculate_adjustment(filters, non_expense_mapper, expense_mapper, use_accumulated_values, period_list):
+ liability_accounts = [d['names'] for d in non_expense_mapper]
+ expense_accounts = [d['names'] for d in expense_mapper]
+
+ non_expense_closing = _get_account_type_based_data(
+ filters, liability_accounts, period_list, 0)
+
+ non_expense_opening = _get_account_type_based_data(
+ filters, liability_accounts, period_list, use_accumulated_values, opening_balances=1)
+
+ expense_data = _get_account_type_based_data(
+ filters, expense_accounts, period_list, use_accumulated_values)
+
+ data = _calculate_adjustment(non_expense_closing, non_expense_opening, expense_data)
+ return data
+
+
+def _calculate_adjustment(non_expense_closing, non_expense_opening, expense_data):
+ account_data = {}
+ for month in non_expense_opening.keys():
+ if non_expense_opening[month] and non_expense_closing[month]:
+ account_data[month] = non_expense_opening[month] - expense_data[month] + non_expense_closing[month]
+ elif expense_data[month]:
+ account_data[month] = expense_data[month]
+
+ return account_data
+
+
+def add_data_for_other_activities(
+ filters, company_currency, profit_data, period_list, light_mappers, mapper_list, data):
+ for mapper in mapper_list:
+ section_data = []
+ data.append({
+ "account_name": mapper['section_header'],
+ "parent_account": None,
+ "indent": 0.0,
+ "account": mapper['section_header']
+ })
+
+ for account in mapper['account_types']:
+ account_data = _get_account_type_based_data(filters,
+ account['names'], period_list, filters.accumulated_values)
+ if account_data['total'] != 0:
+ account_data.update({
+ "account_name": account['label'],
+ "account": account['names'],
+ "indent": 1,
+ "parent_account": mapper['section_header'],
+ "currency": company_currency
+ })
+ data.append(account_data)
+ section_data.append(account_data)
+
+ _add_total_row_account(data, section_data, mapper['section_footer'],
+ period_list, company_currency)
+
+
+def compute_data(filters, company_currency, profit_data, period_list, light_mappers, full_mapper):
+ data = []
+
+ operating_activities_mapper = get_mapper_for(light_mappers, position=0)
+ other_mappers = [
+ get_mapper_for(light_mappers, position=1),
+ get_mapper_for(light_mappers, position=2)
+ ]
+
+ if operating_activities_mapper:
+ add_data_for_operating_activities(
+ filters, company_currency, profit_data, period_list, light_mappers,
+ operating_activities_mapper, data
+ )
+
+ if all(other_mappers):
+ add_data_for_other_activities(
+ filters, company_currency, profit_data, period_list, light_mappers, other_mappers, data
+ )
+
+ return data
+
+
+def execute(filters=None):
+ period_list = get_period_list(
+ filters.from_fiscal_year, filters.to_fiscal_year, filters.periodicity,
+ filters.accumulated_values, filters.company
+ )
+
+ mappers = get_mappers_from_db()
+
+ cash_flow_accounts = setup_mappers(mappers)
+
+ # compute net profit / loss
+ income = get_data(
+ filters.company, "Income", "Credit", period_list,
+ accumulated_values=filters.accumulated_values, ignore_closing_entries=True,
+ ignore_accumulated_values_for_fy=True
+ )
+
+ expense = get_data(
+ filters.company, "Expense", "Debit", period_list,
+ accumulated_values=filters.accumulated_values, ignore_closing_entries=True,
+ ignore_accumulated_values_for_fy=True
+ )
+
+ net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
+
+ company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
+
+ data = compute_data(filters, company_currency, net_profit_loss, period_list, mappers, cash_flow_accounts)
+
+ _add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
+ columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
+
+ return columns, data
+
+
+def _get_account_type_based_data(filters, account_names, period_list, accumulated_values, opening_balances=0):
+ from erpnext.accounts.report.cash_flow.cash_flow import get_start_date
+
+ company = filters.company
+ data = {}
+ total = 0
+ for period in period_list:
+ start_date = get_start_date(period, accumulated_values, company)
+
+ if opening_balances:
+ date_info = dict(date=start_date)
+ months_map = {'Monthly': -1, 'Quarterly': -3, 'Half-Yearly': -6}
+ years_map = {'Yearly': -1}
+
+ if months_map.get(filters.periodicity):
+ date_info.update(months=months_map[filters.periodicity])
+ else:
+ date_info.update(years=years_map[filters.periodicity])
+
+ if accumulated_values:
+ start, end = add_to_date(start_date, years=-1), add_to_date(period['to_date'], years=-1)
+ else:
+ start, end = add_to_date(**date_info), add_to_date(**date_info)
+
+ gl_sum = frappe.db.sql_list("""
+ select sum(credit) - sum(debit)
+ from `tabGL Entry`
+ where company=%s and posting_date >= %s and posting_date <= %s
+ and voucher_type != 'Period Closing Voucher'
+ and account in ( SELECT name FROM tabAccount WHERE name IN %s
+ OR parent_account IN %s)
+ """, (company, start, end, account_names, account_names))
+ else:
+ gl_sum = frappe.db.sql_list("""
+ select sum(credit) - sum(debit)
+ from `tabGL Entry`
+ where company=%s and posting_date >= %s and posting_date <= %s
+ and voucher_type != 'Period Closing Voucher'
+ and account in ( SELECT name FROM tabAccount WHERE name IN %s
+ OR parent_account IN %s)
+ """, (company, start_date if accumulated_values else period['from_date'],
+ period['to_date'], account_names, account_names))
+
+ if gl_sum and gl_sum[0]:
+ amount = gl_sum[0]
+ else:
+ amount = 0
+
+ total += amount
+ data.setdefault(period["key"], amount)
+
+ data["total"] = total
+ return data
+
+
+def _add_total_row_account(out, data, label, period_list, currency, indent=0.0):
+ total_row = {
+ "indent": indent,
+ "account_name": "'" + _("{0}").format(label) + "'",
+ "account": "'" + _("{0}").format(label) + "'",
+ "currency": currency
+ }
+ for row in data:
+ if row.get("parent_account"):
+ for period in period_list:
+ total_row.setdefault(period.key, 0.0)
+ total_row[period.key] += row.get(period.key, 0.0)
+
+ total_row.setdefault("total", 0.0)
+ total_row["total"] += row["total"]
+
+ out.append(total_row)
+ out.append({})
diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
index 89ee63a..466562b 100644
--- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
+++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py
@@ -36,8 +36,8 @@
def get_net_profit_loss(income, expense, period_list, company):
total = 0
net_profit_loss = {
- "account_name": "'" + _("Net Profit / Loss") + "'",
- "account": "'" + _("Net Profit / Loss") + "'",
+ "account_name": "'" + _("Profit for the year") + "'",
+ "account": "'" + _("Profit for the year") + "'",
"warn_if_negative": True,
"currency": frappe.db.get_value("Company", company, "default_currency")
}
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapper-1.png b/erpnext/docs/assets/img/articles/cash-flow-mapper-1.png
new file mode 100644
index 0000000..c6b37b1
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapper-1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapper-2.png b/erpnext/docs/assets/img/articles/cash-flow-mapper-2.png
new file mode 100644
index 0000000..b3ba12d
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapper-2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapper-3.png b/erpnext/docs/assets/img/articles/cash-flow-mapper-3.png
new file mode 100644
index 0000000..f494ecb
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapper-3.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapper-4.png b/erpnext/docs/assets/img/articles/cash-flow-mapper-4.png
new file mode 100644
index 0000000..8f94b87
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapper-4.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapper-5.png b/erpnext/docs/assets/img/articles/cash-flow-mapper-5.png
new file mode 100644
index 0000000..39371e6
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapper-5.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapper-6.png b/erpnext/docs/assets/img/articles/cash-flow-mapper-6.png
new file mode 100644
index 0000000..08472ca
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapper-6.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-1.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-1.png
new file mode 100644
index 0000000..e724b5b
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-10.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-10.png
new file mode 100644
index 0000000..b9ca66f
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-10.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-2.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-2.png
new file mode 100644
index 0000000..89adfb9
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-3.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-3.png
new file mode 100644
index 0000000..45595f0
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-3.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-4.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-4.png
new file mode 100644
index 0000000..0be7f20
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-4.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-5.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-5.png
new file mode 100644
index 0000000..fc51732
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-5.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-7.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-7.png
new file mode 100644
index 0000000..6bf3043
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-7.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-8.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-8.png
new file mode 100644
index 0000000..f945a7d
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-8.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/cash-flow-mapping-9.png b/erpnext/docs/assets/img/articles/cash-flow-mapping-9.png
new file mode 100644
index 0000000..5928909
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/cash-flow-mapping-9.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/default-cash-flow-report.png b/erpnext/docs/assets/img/articles/default-cash-flow-report.png
new file mode 100644
index 0000000..8041781
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/default-cash-flow-report.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/final-cash-flow.png b/erpnext/docs/assets/img/articles/final-cash-flow.png
new file mode 100644
index 0000000..88821c4
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/final-cash-flow.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/format-1.png b/erpnext/docs/assets/img/articles/format-1.png
new file mode 100644
index 0000000..e504455
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/format-1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/format-2.png b/erpnext/docs/assets/img/articles/format-2.png
new file mode 100644
index 0000000..c997f99
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/format-2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/new-cash-flow-mapping.png b/erpnext/docs/assets/img/articles/new-cash-flow-mapping.png
new file mode 100644
index 0000000..df269bc
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/new-cash-flow-mapping.png
Binary files differ
diff --git a/erpnext/docs/assets/img/articles/no-mappers.png b/erpnext/docs/assets/img/articles/no-mappers.png
new file mode 100644
index 0000000..4dd000f
--- /dev/null
+++ b/erpnext/docs/assets/img/articles/no-mappers.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/accounts/articles/how-to-customise-cash-flow-report.md b/erpnext/docs/user/manual/en/accounts/articles/how-to-customise-cash-flow-report.md
new file mode 100644
index 0000000..ed4262a
--- /dev/null
+++ b/erpnext/docs/user/manual/en/accounts/articles/how-to-customise-cash-flow-report.md
@@ -0,0 +1,156 @@
+# How To Customise Cash Flow Report
+
+As your chart of accounts begins to get more complex and reporting standards change and evolve, the default cash flow
+report might no longer suffice. This is because ERPNext might not be able to accurately guess the classification and
+purpose of all accounts in the charts of accounts. Another gripe you might have is the inability to adjust the report
+format to fit your needs.
+
+This will no longer be a problem because ERPNext now allows users to customise the cash flow report.
+
+
+## Technical Overview
+Customisation is made possible by the introduction of two new doctypes - Cash Flow Mapper and Cash Flow Mapping. Both
+doctypes contain the information required to generate a cash flow report.
+
+Cash Flow Mapping shows how accounts in your charts of accounts map to a line item in your cash flow report while
+Cash Flow Mapper gets all the Cash Flow Mappings that relate to the three sections of a cash flow statement.
+
+With this, you generate detailed cash flow reports to your requirements. This might not make a lot of sense but it will
+after we go through an example.
+
+## Example
+### Background information
+Let's assume we have a fictitious company for which we want to generate a cash flow report.
+This is what the cash flow report looks like at the moment:
+<img alt="Default cash flow report" class="screenshot" src="{{docs_base_url}}/assets/img/articles/default-cash-flow-report.png">
+
+We don't like the report for the following reasons:
+- The reporting format is too scant.
+- The 'Net Cash From Operations' figure is wrong
+
+### Customisation Process
+
+We wants the Cash Flow Report to look something similar to the format in the images below:
+<img alt="cash flow format 1" class="screenshot" src="{{docs_base_url}}/assets/img/articles/format-1.png">
+<img alt="cash flow format 1" class="screenshot" src="{{docs_base_url}}/assets/img/articles/format-2.png">
+
+#### Activate Customised Cash Flow Report
+Do this in Accounts Settings by checking the 'Use Custom Cash Flow Format' checkbox. This will cause ERPNext to only
+use your custom format for cash flow reports.
+
+After doing that, your cash flow report should look like this:
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/no-mappers.png">
+
+Move to the next section to build the report.
+
+#### Create Cash Flow Mappings
+For each line, we need to create a Cash Flow Mapping document to represent it.
+
+<img alt="new cash flow mapping form" class="screenshot" src="{{docs_base_url}}/assets/img/articles/new-cash-flow-mapping.png">
+
+You can think of the Cash Flow Mapping as a representation of each line in the cash flow report. A Cash Flow Mapping
+is a child of a Cash Flow Mapper which will be explained later.
+
+Let's start by creating Cash Flow Mappings that will represent the add back of non cash expenses already recodgnised in
+the Profit or Loss statement. We want them to appear on the cash statement as:
+- Income taxes recognised in profit or loss
+- Finance costs recognised in profit or loss
+- Depreciation of non-current assets
+
+Start by opening a new Cash Flow Mapping form.
+
+The fields in the Cash Flow Mapping doctype are:
+- **Name**: This something to identify this document. Name it something related to the label
+- **Label**: This is what will show in the cash flow statement
+- **Accounts**: This table contains all the accounts which this line relates to.
+
+With this information, let's go ahead and create the Cash Flow Mapping Document for the line 'Income taxes recognised in profit or loss'
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-1.png">
+
+I have named it 'Income Tax Charge' and given it a label 'Income taxes recognised in profit or loss'. We want this
+line to reflect income tax charges from our profit or loss statement. The account where this happens in our chart
+of account is named 'Income Taxes' (an expense) so I have added 'Income Taxes' into the accounts table. If you have
+more accounts representing income tax expenses, you should add all of them here.
+
+Because Income Tax expense needs to be adjusted further in the cash flow statement, check the 'Is Income Tax Expense'
+checkbox. This is what will help ERPNext properly calculate the adjustments to be made.
+
+*For best results, let parent accounts have child accounts that have the same treatment for cash flow reporting
+purposes because ERPNext will calculate net change of all children accounts in a situation where the selected account
+is a parent account.*
+
+In the same way, I have created for the remaining two mappings.
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-2.png">
+
+Finance costs also need to be adjusted so make sure to check the 'Is Finance Cost' checkbox.
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-3.png">
+
+Next let's add Cash Flow Mapping for items that show changes in working capital:
+- Increase/(decrease) in other liabilities
+- (Increase)/decrease in trade and other receivables
+- Increase/(decrease) in trade and other payables
+- VAT payable
+- (Increase)/decrease in inventory
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-4.png">
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-5.png">
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-6.png">
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-7.png">
+
+<img alt="custom cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-8.png">
+
+Don't forget to tell ERPNext that these mappings represent changes in working capital by checking the 'Is Working
+Capital' checkbox.
+
+At this point we have created all the mappings necessary for the Operating Activities section of our cash flow
+statement. However, ERPNext doesn't know that yet until we create Cash Flow Mapper documents. We'll create Cash Flow
+Mapper documents next.
+
+
+#### Create Cash Flow Mappers
+Cash Flow Mappers represents the sections of the cash flow statement. A standard cash flow statement has only three
+sections so when you view the Cash Flow Mapper list, you will that three have been created for you named:
+- Operating Activities
+- Financing Activities
+- Investing Activities
+
+You will not be able to add or remove any of them but they are editable and can be renamed.
+<img alt="cash flow mapper list" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapper-2.png">
+
+
+Open the Operating Activities Cash Flow Mapper so we can add the Cash Flow Mappings we have created.
+
+
+- **Section Name**: This is the heading of the section.
+- **Section Leader**: This is the first sub-header immediately after the profit figure. Relates only to Operating
+Activities Cash Flow Mapper
+- **Section Subtotal**: This is the label for subtotal in the cash flow statement section. Relates only to Operating
+Activities Cash Flow Mapper
+- **Section Footer**: This is the label for the total in the cash flow statement section.
+- **Mapping**: This table contains all the Cash Flow Mappings related to the Cash Flow Mapper.
+
+Now add all the Cash Flow Mappings you have created and Save. You should have something like this:
+<img alt="cash flow mapper for operating activities" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapper-4.png">
+
+ Refresh the cash flow statement and view the changes.
+<img alt="updated cash flow report" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapper-3.png">
+
+Looks close to our requirements but we are not done yet. Create new mappings for 'Investing Activities' and 'Financing
+Activities' sections of the cash flow statement.
+
+<img alt="cash flow mapping" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-9.png">
+
+<img alt="cash flow mapping" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapping-10.png">
+
+<img alt="cash flow mapper for operating activities" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapper-5.png">
+
+<img alt="cash flow mapper for operating activities" class="screenshot" src="{{docs_base_url}}/assets/img/articles/cash-flow-mapper-6.png">
+
+Here's what our cash flow statement now looks like:
+<img alt="final cash flow statement" class="screenshot" src="{{docs_base_url}}/assets/img/articles/final-cash-flow.png">
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index b365e96..c4e3949 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -502,5 +502,6 @@
erpnext.patches.v10_0.rename_offer_letter_to_job_offer
execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True)
erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group
+erpnext.patches.v10_0.add_default_cash_flow_mappers
erpnext.patches.v11_0.make_quality_inspection_template
erpnext.patches.v10_0.update_territory_and_customer_group
diff --git a/erpnext/patches/v10_0/add_default_cash_flow_mappers.py b/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
new file mode 100644
index 0000000..d607b2f
--- /dev/null
+++ b/erpnext/patches/v10_0/add_default_cash_flow_mappers.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from erpnext.setup.install import create_default_cash_flow_mapper_templates
+
+
+def execute():
+ frappe.reload_doc('accounts', 'doctype', frappe.scrub('Cash Flow Mapping'))
+ frappe.reload_doc('accounts', 'doctype', frappe.scrub('Cash Flow Mapper'))
+ frappe.reload_doc('accounts', 'doctype', frappe.scrub('Cash Flow Mapping Template Details'))
+
+ create_default_cash_flow_mapper_templates()
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 0c93be9..8548f92 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -4,6 +4,7 @@
from __future__ import print_function, unicode_literals
import frappe
+from erpnext.accounts.doctype.cash_flow_mapper.default_cash_flow_mapper import DEFAULT_MAPPERS
from frappe import _
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
@@ -11,14 +12,17 @@
default_mail_footer = """<div style="padding: 7px; text-align: right; color: #888"><small>Sent via
<a style="color: #888" href="http://erpnext.org">ERPNext</a></div>"""
+
def after_install():
frappe.get_doc({'doctype': "Role", "role_name": "Analytics"}).insert()
set_single_defaults()
create_compact_item_print_custom_field()
create_print_zero_amount_taxes_custom_field()
add_all_roles_to("Administrator")
+ create_default_cash_flow_mapper_templates()
frappe.db.commit()
+
def check_setup_wizard_not_completed():
if frappe.db.get_default('desktop:home_page') == 'desktop':
print()
@@ -27,6 +31,7 @@
print()
return False
+
def set_single_defaults():
for dt in ('Accounts Settings', 'Print Settings', 'HR Settings', 'Buying Settings',
'Selling Settings', 'Stock Settings'):
@@ -45,6 +50,7 @@
frappe.db.set_default("date_format", "dd-mm-yyyy")
+
def create_compact_item_print_custom_field():
create_custom_field('Print Settings', {
'label': _('Compact Item Print'),
@@ -54,6 +60,7 @@
'insert_after': 'with_letterhead'
})
+
def create_print_zero_amount_taxes_custom_field():
create_custom_field('Print Settings', {
'label': _('Print taxes with zero amount'),
@@ -61,4 +68,13 @@
'fieldtype': 'Check',
'default': 0,
'insert_after': 'allow_print_for_cancelled'
- })
\ No newline at end of file
+ })
+
+
+def create_default_cash_flow_mapper_templates():
+ mappers = DEFAULT_MAPPERS
+
+ for mapper in mappers:
+ if not frappe.db.exists('Cash Flow Mapper', mapper['section_name']):
+ doc = frappe.get_doc(mapper)
+ doc.insert(ignore_permissions=True)