fix: merge conflict

This commit is contained in:
Nabin Hait 2021-03-11 16:45:41 +05:30
commit fe3529b194
564 changed files with 21204 additions and 16877 deletions

View File

@ -92,6 +92,7 @@
"cur_page": true,
"cur_list": true,
"cur_tree": true,
"cur_pos": true,
"msg_dialog": true,
"is_null": true,
"in_list": true,
@ -149,6 +150,7 @@
"it": true,
"context": true,
"before": true,
"beforeEach": true
"beforeEach": true,
"onScan": true
}
}

View File

@ -2,7 +2,7 @@ import re
import sys
errors_encounter = 0
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
pattern = re.compile(r"_\(([\"']{,3})(?P<message>((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P<py_context>((?!\5).)*)\5)*(\s*,(\s*?.*?\n*?)*(,\s*([\"'])(?P<js_context>((?!\11).)*)\11)*)*\)")
words_pattern = re.compile(r"_{1,2}\([\"'`]{1,3}.*?[a-zA-Z]")
start_pattern = re.compile(r"_{1,2}\([f\"'`]{1,3}")
f_string_pattern = re.compile(r"_\(f[\"']")
@ -28,7 +28,7 @@ for _file in files_to_scan:
has_f_string = f_string_pattern.search(line)
if has_f_string:
errors_encounter += 1
print(f'\nF-strings are not supported for translations at line number {line_number + 1}\n{line.strip()[:100]}')
print(f'\nF-strings are not supported for translations at line number {line_number}\n{line.strip()[:100]}')
continue
else:
continue
@ -36,7 +36,7 @@ for _file in files_to_scan:
match = pattern.search(line)
error_found = False
if not match and line.endswith(',\n'):
if not match and line.endswith((',\n', '[\n')):
# concat remaining text to validate multiline pattern
line = "".join(file_lines[line_number - 1:])
line = line[start_matches.start() + 1:]
@ -44,11 +44,11 @@ for _file in files_to_scan:
if not match:
error_found = True
print(f'\nTranslation syntax error at line number {line_number + 1}\n{line.strip()[:100]}')
print(f'\nTranslation syntax error at line number {line_number}\n{line.strip()[:100]}')
if not error_found and not words_pattern.search(line):
error_found = True
print(f'\nTranslation is useless because it has no words at line number {line_number + 1}\n{line.strip()[:100]}')
print(f'\nTranslation is useless because it has no words at line number {line_number}\n{line.strip()[:100]}')
if error_found:
errors_encounter += 1

9
erpnext/.stylelintrc Normal file
View File

@ -0,0 +1,9 @@
{
"extends": ["stylelint-config-recommended"],
"plugins": ["stylelint-scss"],
"rules": {
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": true,
"no-descending-specificity": null
}
}

View File

@ -109,7 +109,7 @@ def get_region(company=None):
'''
if company or frappe.flags.company:
return frappe.get_cached_value('Company',
company or frappe.flags.company, 'country')
company or frappe.flags.company, 'country')
elif frappe.flags.country:
return frappe.flags.country
else:

View File

@ -1,161 +0,0 @@
{
"cards": [
{
"hidden": 0,
"label": "Accounting Masters",
"links": "[\n {\n \"description\": \"Company (not Customer or Supplier) master.\",\n \"label\": \"Company\",\n \"name\": \"Company\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of financial accounts.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Accounts\",\n \"name\": \"Account\",\n \"onboard\": 1,\n \"route\": \"#Tree/Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounts Settings\",\n \"name\": \"Accounts Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Financial / accounting year.\",\n \"label\": \"Fiscal Year\",\n \"name\": \"Fiscal Year\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Finance Book\",\n \"name\": \"Finance Book\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Period\",\n \"name\": \"Accounting Period\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Terms based on conditions\",\n \"label\": \"Payment Term\",\n \"name\": \"Payment Term\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "General Ledger",
"links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make journal entries from a template.\",\n \"label\": \"Journal Entry Template\",\n \"name\": \"Journal Entry Template\",\n \"type\": \"doctype\"\n },\n \n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Accounts Receivable",
"links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Accounts Payable",
"links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Reports",
"links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"DATEV Export\",\n \"name\": \"DATEV\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Financial Statements",
"links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance\",\n \"name\": \"Trial Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profit and Loss Statement\",\n \"name\": \"Profit and Loss Statement\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Balance Sheet\",\n \"name\": \"Balance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Cash Flow\",\n \"name\": \"Cash Flow\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Consolidated Financial Statement\",\n \"name\": \"Consolidated Financial Statement\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Multi Currency",
"links": "[\n {\n \"description\": \"Enable / disable currencies.\",\n \"label\": \"Currency\",\n \"name\": \"Currency\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Currency exchange rate master.\",\n \"label\": \"Currency Exchange\",\n \"name\": \"Currency Exchange\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Exchange Rate Revaluation master.\",\n \"label\": \"Exchange Rate Revaluation\",\n \"name\": \"Exchange Rate Revaluation\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Settings",
"links": "[\n {\n \"description\": \"Setup Gateway accounts.\",\n \"label\": \"Payment Gateway Account\",\n \"name\": \"Payment Gateway Account\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"e.g. Bank, Cash, Credit Card\",\n \"label\": \"Mode of Payment\",\n \"name\": \"Mode of Payment\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Bank Statement",
"links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Subscription Management",
"links": "[\n {\n \"label\": \"Subscription Plan\",\n \"name\": \"Subscription Plan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription\",\n \"name\": \"Subscription\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Subscription Settings\",\n \"name\": \"Subscription Settings\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Goods and Services Tax (GST India)",
"links": "[\n {\n \"label\": \"GST Settings\",\n \"name\": \"GST Settings\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"GST HSN Code\",\n \"name\": \"GST HSN Code\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-1\",\n \"name\": \"GSTR-1\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GSTR-2\",\n \"name\": \"GSTR-2\",\n \"type\": \"report\"\n },\n {\n \"label\": \"GSTR 3B Report\",\n \"name\": \"GSTR 3B Report\",\n \"type\": \"doctype\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Sales Register\",\n \"name\": \"GST Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Purchase Register\",\n \"name\": \"GST Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Sales Register\",\n \"name\": \"GST Itemised Sales Register\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"GST Itemised Purchase Register\",\n \"name\": \"GST Itemised Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"country\": \"India\",\n \"description\": \"C-Form records\",\n \"label\": \"C-Form\",\n \"name\": \"C-Form\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"India\",\n \"label\": \"Lower Deduction Certificate\",\n \"name\": \"Lower Deduction Certificate\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Share Management",
"links": "[\n {\n \"description\": \"List of available Shareholders with folio numbers\",\n \"label\": \"Shareholder\",\n \"name\": \"Shareholder\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"List of all share transactions\",\n \"label\": \"Share Transfer\",\n \"name\": \"Share Transfer\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Ledger\",\n \"name\": \"Share Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Share Transfer\"\n ],\n \"doctype\": \"Share Transfer\",\n \"is_query_report\": true,\n \"label\": \"Share Balance\",\n \"name\": \"Share Balance\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Cost Center and Budgeting",
"links": "[\n {\n \"description\": \"Tree of financial Cost Centers.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Chart of Cost Centers\",\n \"name\": \"Cost Center\",\n \"route\": \"#Tree/Cost Center\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define budget for a financial year.\",\n \"label\": \"Budget\",\n \"name\": \"Budget\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Accounting Dimension\",\n \"name\": \"Accounting Dimension\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Cost Center\"\n ],\n \"doctype\": \"Cost Center\",\n \"is_query_report\": true,\n \"label\": \"Budget Variance Report\",\n \"name\": \"Budget Variance Report\",\n \"type\": \"report\"\n },\n {\n \"description\": \"Seasonality for setting budgets, targets etc.\",\n \"label\": \"Monthly Distribution\",\n \"name\": \"Monthly Distribution\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Opening and Closing",
"links": "[\n {\n \"label\": \"Opening Invoice Creation Tool\",\n \"name\": \"Opening Invoice Creation Tool\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Chart of Accounts Importer\",\n \"name\": \"Chart of Accounts Importer\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Close Balance Sheet and book Profit or Loss.\",\n \"label\": \"Period Closing Voucher\",\n \"name\": \"Period Closing Voucher\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Taxes",
"links": "[\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for item tax rates.\",\n \"label\": \"Item Tax Template\",\n \"name\": \"Item Tax Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Category for overriding tax rates.\",\n \"label\": \"Tax Category\",\n \"name\": \"Tax Category\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Rule for transactions.\",\n \"label\": \"Tax Rule\",\n \"name\": \"Tax Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax Withholding rates to be applied on transactions.\",\n \"label\": \"Tax Withholding Category\",\n \"name\": \"Tax Withholding Category\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
"label": "Profitability",
"links": "[\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Gross Profit\",\n \"name\": \"Gross Profit\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Profitability Analysis\",\n \"name\": \"Profitability Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
"label": "Value-Added Tax (VAT UAE)",
"links": "[\n {\n \"country\": \"United Arab Emirates\",\n \"label\": \"UAE VAT Settings\",\n \"name\": \"UAE VAT Settings\",\n \"type\": \"doctype\"\n },\n {\n \"country\": \"United Arab Emirates\",\n \"is_query_report\": true,\n \"label\": \"UAE VAT 201\",\n \"name\": \"UAE VAT 201\",\n \"type\": \"report\"\n }\n\n]"
}
],
"category": "Modules",
"charts": [
{
"chart_name": "Profit and Loss",
"label": "Profit and Loss"
}
],
"creation": "2020-03-02 15:41:59.515192",
"developer_mode_only": 0,
"disable_user_customization": 0,
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
"hide_custom": 0,
"idx": 0,
"is_standard": 1,
"label": "Accounting",
"modified": "2020-11-11 18:35:11.542909",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
"onboarding": "Accounts",
"owner": "Administrator",
"pin_to_bottom": 0,
"pin_to_top": 0,
"shortcuts": [
{
"label": "Chart Of Accounts",
"link_to": "Account",
"type": "DocType"
},
{
"label": "Sales Invoice",
"link_to": "Sales Invoice",
"type": "DocType"
},
{
"label": "Purchase Invoice",
"link_to": "Purchase Invoice",
"type": "DocType"
},
{
"label": "Journal Entry",
"link_to": "Journal Entry",
"type": "DocType"
},
{
"label": "Payment Entry",
"link_to": "Payment Entry",
"type": "DocType"
},
{
"label": "Accounts Receivable",
"link_to": "Accounts Receivable",
"type": "Report"
},
{
"label": "General Ledger",
"link_to": "General Ledger",
"type": "Report"
},
{
"label": "Trial Balance",
"link_to": "Trial Balance",
"type": "Report"
},
{
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
}
]
}

View File

@ -120,17 +120,17 @@ frappe.treeview_settings["Account"] = {
} else {
treeview.new_node();
}
}, "octicon octicon-plus");
}, "add");
},
onrender: function(node) {
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
if (frappe.boot.user.can_read.indexOf("GL Entry") !== -1) {
// show Dr if positive since balance is calculated as debit - credit else show Cr
let balance = node.data.balance_in_account_currency || node.data.balance;
let dr_or_cr = balance > 0 ? "Dr": "Cr";
if (node.data && node.data.balance!==undefined) {
$('<span class="balance-area pull-right text-muted small">'
$('<span class="balance-area pull-right">'
+ (node.data.balance_in_account_currency ?
(format_currency(Math.abs(node.data.balance_in_account_currency),
node.data.account_currency) + " / ") : "")

View File

@ -63,17 +63,21 @@
"Gewinnermittlung \u00a74/3 nicht Ergebniswirksam": {
"account_number": "1371"
},
"Abziehbare VSt. 7%": {
"account_number": "1571"
},
"Abziehbare VSt. 19%": {
"account_number": "1576"
},
"Abziehbare VStr. nach \u00a713b UStG 19%": {
"account_number": "1577"
},
"Leistungen \u00a713b UStG 19% Vorsteuer, 19% Umsatzsteuer": {
"account_number": "3120"
"Abziehbare Vorsteuer": {
"account_type": "Tax",
"is_group": 1,
"Abziehbare Vorsteuer 7%": {
"account_number": "1571"
},
"Abziehbare Vorsteuer 19%": {
"account_number": "1576"
},
"Abziehbare Vorsteuer nach \u00a713b UStG 19%": {
"account_number": "1577"
},
"Leistungen \u00a713b UStG 19% Vorsteuer, 19% Umsatzsteuer": {
"account_number": "3120"
}
}
},
"III. Wertpapiere": {
@ -196,6 +200,7 @@
},
"Umsatzsteuer": {
"is_group": 1,
"account_type": "Tax",
"Umsatzsteuer 7%": {
"account_number": "1771"
},

View File

@ -292,18 +292,21 @@
"Umsatzsteuerforderungen fr\u00fchere Jahre": {}
},
"Sonstige Verm\u00f6gensgegenst\u00e4nde oder sonstige Verbindlichkeiten": {
"Abziehbare Vorsteuer": {},
"Abziehbare Vorsteuer 16%": {},
"Abziehbare Vorsteuer 19%": {},
"Abziehbare Vorsteuer 7%": {},
"Abziehbare Vorsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Unsatzsteuerlager": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 16%": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 19%": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb von Neufahrzeugen von Lieferanten ohne Ust-Identifikationsnummer": {},
"Abziehbare Vorsteuer nach \u00a7 13b UStG ": {},
"Abziehbare Vorsteuer nach \u00a7 13b UStG 16%": {},
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19%": {},
"Abziehbare Vorsteuer": {
"account_type": "Tax",
"is_group": 1,
"Abziehbare Vorsteuer 16%": {},
"Abziehbare Vorsteuer 19%": {},
"Abziehbare Vorsteuer 7%": {},
"Abziehbare Vorsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Unsatzsteuerlager": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 16%": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb 19%": {},
"Abziehbare Vorsteuer aus innergemeinschaftlichem Erwerb von Neufahrzeugen von Lieferanten ohne Ust-Identifikationsnummer": {},
"Abziehbare Vorsteuer nach \u00a7 13b UStG ": {},
"Abziehbare Vorsteuer nach \u00a7 13b UStG 16%": {},
"Abziehbare Vorsteuer nach \u00a7 13b UStG 19%": {}
},
"Aufl\u00f6sung Vorsteuer aus Vorjahr \u00a7 4/3 EStG": {},
"Aufzuteilende Vorsteuer": {},
"Aufzuteilende Vorsteuer 16%": {},
@ -673,23 +676,26 @@
"Sonstige Verrechnungskonten (Interimskonto)": {
"account_type": "Stock Received But Not Billed"
},
"Umsatzsteuer": {},
"Umsatzsteuer 16%": {},
"Umsatzsteuer 19%": {},
"Umsatzsteuer 7%": {},
"Umsatzsteuer Vorjahr": {},
"Umsatzsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Umsatzsteuerlager": {},
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen": {},
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen 19%": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 16%": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 19%": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ohne Vorsteuerabzug": {},
"Umsatzsteuer fr\u00fchere Jahre": {},
"Umsatzsteuer laufendes Jahr": {},
"Umsatzsteuer nach \u00a713b UStG": {},
"Umsatzsteuer nach \u00a713b UStG 16%": {},
"Umsatzsteuer nach \u00a713b UStG 19%": {},
"Umsatzsteuer": {
"account_type": "Tax",
"is_group": 1,
"Umsatzsteuer 16%": {},
"Umsatzsteuer 19%": {},
"Umsatzsteuer 7%": {},
"Umsatzsteuer Vorjahr": {},
"Umsatzsteuer aus der Auslagerung von Gegenst\u00e4nden aus einem Umsatzsteuerlager": {},
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen": {},
"Umsatzsteuer aus im Inland steuerpflichtigen EG-Lieferungen 19%": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 16%": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb 19%": {},
"Umsatzsteuer aus innergemeinschaftlichem Erwerb ohne Vorsteuerabzug": {},
"Umsatzsteuer fr\u00fchere Jahre": {},
"Umsatzsteuer laufendes Jahr": {},
"Umsatzsteuer nach \u00a713b UStG": {},
"Umsatzsteuer nach \u00a713b UStG 16%": {},
"Umsatzsteuer nach \u00a713b UStG 19%": {}
},
"Umsatzsteuer- Vorauszahlungen": {},
"Umsatzsteuer- Vorauszahlungen 1/11": {},
"Verbindlichkeiten aus Lohn- und Kirchensteuer": {}

View File

@ -659,6 +659,7 @@
},
"Abziehbare Vorsteuer (Gruppe)": {
"is_group": 1,
"account_type": "Tax",
"Abziehbare Vorsteuer": {
"account_number": "1400"
},

View File

@ -30,6 +30,7 @@
"fieldtype": "Link",
"label": "Reference Document Type",
"options": "DocType",
"read_only_depends_on": "eval:!doc.__islocal",
"reqd": 1
},
{
@ -48,7 +49,7 @@
}
],
"links": [],
"modified": "2020-03-22 20:34:39.805728",
"modified": "2021-02-08 16:37:53.936656",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting Dimension",

View File

@ -29,6 +29,16 @@ class AccountingDimension(Document):
if exists and self.is_new():
frappe.throw("Document Type already used as a dimension")
if not self.is_new():
self.validate_document_type_change()
def validate_document_type_change(self):
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
if doctype_before_save != self.document_type:
message = _("Cannot change Reference Document Type.")
message += _("Please create a new Accounting Dimension if required.")
frappe.throw(message)
def after_insert(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
@ -51,8 +61,10 @@ class AccountingDimension(Document):
def on_update(self):
frappe.flags.accounting_dimensions = None
def make_dimension_in_accounting_doctypes(doc):
doclist = get_doctypes_with_dimensions()
def make_dimension_in_accounting_doctypes(doc, doclist=None):
if not doclist:
doclist = get_doctypes_with_dimensions()
doc_count = len(get_accounting_dimensions())
count = 0
@ -72,13 +84,13 @@ def make_dimension_in_accounting_doctypes(doc):
"owner": "Administrator"
}
if doctype == "Budget":
add_dimension_to_budget_doctype(df, doc)
else:
meta = frappe.get_meta(doctype, cached=False)
fieldnames = [d.fieldname for d in meta.get("fields")]
meta = frappe.get_meta(doctype, cached=False)
fieldnames = [d.fieldname for d in meta.get("fields")]
if df['fieldname'] not in fieldnames:
if df['fieldname'] not in fieldnames:
if doctype == "Budget":
add_dimension_to_budget_doctype(df.copy(), doc)
else:
create_custom_field(doctype, df)
count += 1
@ -168,15 +180,7 @@ def toggle_disabling(doc):
frappe.clear_cache(doctype=doctype)
def get_doctypes_with_dimensions():
doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
"Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
"Subscription Plan"]
return doclist
return frappe.get_hooks("accounting_dimension_doctypes")
def get_accounting_dimensions(as_list=True):
if frappe.flags.accounting_dimensions is None:

View File

@ -6,3 +6,46 @@ frappe.ui.form.on('Accounts Settings', {
}
});
frappe.tour['Accounts Settings'] = [
{
fieldname: "acc_frozen_upto",
title: "Accounts Frozen Upto",
description: __("Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role."),
},
{
fieldname: "frozen_accounts_modifier",
title: "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
description: __("Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.")
},
{
fieldname: "determine_address_tax_category_from",
title: "Determine Address Tax Category From",
description: __("Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.")
},
{
fieldname: "over_billing_allowance",
title: "Over Billing Allowance Percentage",
description: __("The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.")
},
{
fieldname: "credit_controller",
title: "Credit Controller",
description: __("Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.")
},
{
fieldname: "make_payment_via_journal_entry",
title: "Make Payment via Journal Entry",
description: __("When checked, if user proceeds to make payment from an invoice, the system will open a Journal Entry instead of a Payment Entry.")
},
{
fieldname: "unlink_payment_on_cancellation_of_invoice",
title: "Unlink Payment on Cancellation of Invoice",
description: __("If checked, system will unlink the payment against the respective invoice.")
},
{
fieldname: "unlink_advance_payment_on_cancelation_of_order",
title: "Unlink Advance Payment on Cancellation of Order",
description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.")
}
];

View File

@ -86,6 +86,7 @@
},
{
"default": "0",
"description": "Setting the account as a Company Account is necessary for Bank Reconciliation",
"fieldname": "is_company_account",
"fieldtype": "Check",
"label": "Is Company Account"
@ -207,7 +208,7 @@
}
],
"links": [],
"modified": "2020-07-17 13:59:50.795412",
"modified": "2020-10-23 16:48:06.303658",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Account",

View File

@ -0,0 +1,162 @@
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.provide("erpnext.accounts.bank_reconciliation");
frappe.ui.form.on("Bank Reconciliation Tool", {
setup: function (frm) {
frm.set_query("bank_account", function () {
return {
filters: {
company: ["in", frm.doc.company],
},
};
});
},
refresh: function (frm) {
frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
frm.trigger("make_reconciliation_tool")
);
frm.upload_statement_button = frm.page.set_secondary_action(
__("Upload Bank Statement"),
() =>
frappe.call({
method:
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
args: {
dt: frm.doc.doctype,
dn: frm.doc.name,
company: frm.doc.company,
bank_account: frm.doc.bank_account,
},
callback: function (r) {
if (!r.exc) {
var doc = frappe.model.sync(r.message);
frappe.set_route(
"Form",
doc[0].doctype,
doc[0].name
);
}
},
})
);
},
after_save: function (frm) {
frm.trigger("make_reconciliation_tool");
},
bank_account: function (frm) {
frappe.db.get_value(
"Bank Account",
frm.bank_account,
"account",
(r) => {
frappe.db.get_value(
"Account",
r.account,
"account_currency",
(r) => {
frm.currency = r.account_currency;
}
);
}
);
frm.trigger("get_account_opening_balance");
},
bank_statement_from_date: function (frm) {
frm.trigger("get_account_opening_balance");
},
make_reconciliation_tool(frm) {
frm.get_field("reconciliation_tool_cards").$wrapper.empty();
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
frm.trigger("get_cleared_balance").then(() => {
if (
frm.doc.bank_account &&
frm.doc.bank_statement_from_date &&
frm.doc.bank_statement_to_date &&
frm.doc.bank_statement_closing_balance
) {
frm.trigger("render_chart");
frm.trigger("render");
frappe.utils.scroll_to(
frm.get_field("reconciliation_tool_cards").$wrapper,
true,
30
);
}
});
}
},
get_account_opening_balance(frm) {
if (frm.doc.bank_account && frm.doc.bank_statement_from_date) {
frappe.call({
method:
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
args: {
bank_account: frm.doc.bank_account,
till_date: frm.doc.bank_statement_from_date,
},
callback: (response) => {
frm.set_value("account_opening_balance", response.message);
},
});
}
},
get_cleared_balance(frm) {
if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
return frappe.call({
method:
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
args: {
bank_account: frm.doc.bank_account,
till_date: frm.doc.bank_statement_to_date,
},
callback: (response) => {
frm.cleared_balance = response.message;
},
});
}
},
render_chart(frm) {
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
{
$reconciliation_tool_cards: frm.get_field(
"reconciliation_tool_cards"
).$wrapper,
bank_statement_closing_balance:
frm.doc.bank_statement_closing_balance,
cleared_balance: frm.cleared_balance,
currency: frm.currency,
}
);
},
render(frm) {
if (frm.doc.bank_account) {
frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager(
{
company: frm.doc.company,
bank_account: frm.doc.bank_account,
$reconciliation_tool_dt: frm.get_field(
"reconciliation_tool_dt"
).$wrapper,
$no_bank_transactions: frm.get_field(
"no_bank_transactions"
).$wrapper,
bank_statement_from_date: frm.doc.bank_statement_from_date,
bank_statement_to_date: frm.doc.bank_statement_to_date,
bank_statement_closing_balance:
frm.doc.bank_statement_closing_balance,
cards_manager: frm.cards_manager,
}
);
}
},
});

View File

@ -0,0 +1,113 @@
{
"actions": [],
"creation": "2020-12-02 10:13:02.148040",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"bank_account",
"column_break_1",
"bank_statement_from_date",
"bank_statement_to_date",
"column_break_2",
"account_opening_balance",
"bank_statement_closing_balance",
"section_break_1",
"reconciliation_tool_cards",
"reconciliation_tool_dt",
"no_bank_transactions"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company"
},
{
"fieldname": "bank_account",
"fieldtype": "Link",
"label": "Bank Account",
"options": "Bank Account"
},
{
"fieldname": "column_break_1",
"fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.bank_account",
"fieldname": "bank_statement_from_date",
"fieldtype": "Date",
"label": "Bank Statement From Date"
},
{
"depends_on": "eval: doc.bank_statement_from_date",
"fieldname": "bank_statement_to_date",
"fieldtype": "Date",
"label": "Bank Statement To Date"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.bank_statement_from_date",
"fieldname": "account_opening_balance",
"fieldtype": "Currency",
"label": "Account Opening Balance",
"options": "Currency",
"read_only": 1
},
{
"depends_on": "eval: doc.bank_statement_to_date",
"fieldname": "bank_statement_closing_balance",
"fieldtype": "Currency",
"label": "Bank Statement Closing Balance",
"options": "Currency"
},
{
"depends_on": "eval: doc.bank_statement_closing_balance",
"fieldname": "section_break_1",
"fieldtype": "Section Break",
"label": "Reconcile"
},
{
"fieldname": "reconciliation_tool_cards",
"fieldtype": "HTML"
},
{
"fieldname": "reconciliation_tool_dt",
"fieldtype": "HTML"
},
{
"fieldname": "no_bank_transactions",
"fieldtype": "HTML",
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
}
],
"hide_toolbar": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-02-02 01:35:53.043578",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation Tool",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"print": 1,
"read": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,452 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import json
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.utils import flt
from erpnext import get_company_currency
from erpnext.accounts.utils import get_balance_on
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import get_entries, get_amounts_not_reflected_in_system
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
class BankReconciliationTool(Document):
pass
@frappe.whitelist()
def get_bank_transactions(bank_account, from_date = None, to_date = None):
# returns bank transactions for a bank account
filters = []
filters.append(['bank_account', '=', bank_account])
filters.append(['docstatus', '=', 1])
filters.append(['unallocated_amount', '>', 0])
if to_date:
filters.append(['date', '<=', to_date])
if from_date:
filters.append(['date', '>=', from_date])
transactions = frappe.get_all(
'Bank Transaction',
fields = ['date', 'deposit', 'withdrawal', 'currency',
'description', 'name', 'bank_account', 'company',
'unallocated_amount', 'reference_number', 'party_type', 'party'],
filters = filters
)
return transactions
@frappe.whitelist()
def get_account_balance(bank_account, till_date):
# returns account balance till the specified date
account = frappe.db.get_value('Bank Account', bank_account, 'account')
filters = frappe._dict({
"account": account,
"report_date": till_date,
"include_pos_transactions": 1
})
data = get_entries(filters)
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
total_debit, total_credit = 0,0
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
+ amounts_not_reflected_in_system
return bank_bal
@frappe.whitelist()
def update_bank_transaction(bank_transaction_name, reference_number, party_type=None, party=None):
# updates bank transaction based on the new parameters provided by the user from Vouchers
bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_transaction.reference_number = reference_number
bank_transaction.party_type = party_type
bank_transaction.party = party
bank_transaction.save()
return frappe.db.get_all('Bank Transaction',
filters={
'name': bank_transaction_name
},
fields=['date', 'deposit', 'withdrawal', 'currency',
'description', 'name', 'bank_account', 'company',
'unallocated_amount', 'reference_number',
'party_type', 'party'],
)[0]
@frappe.whitelist()
def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None,
second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None):
# Create a new journal entry based on the bank transaction
bank_transaction = frappe.db.get_values(
"Bank Transaction", bank_transaction_name,
fieldname=["name", "deposit", "withdrawal", "bank_account"] ,
as_dict=True
)[0]
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
account_type = frappe.db.get_value("Account", second_account, "account_type")
if account_type in ["Receivable", "Payable"]:
if not (party_type and party):
frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account))
accounts = []
# Multi Currency?
accounts.append({
"account": second_account,
"credit_in_account_currency": bank_transaction.deposit
if bank_transaction.deposit > 0
else 0,
"debit_in_account_currency":bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"party_type":party_type,
"party":party,
})
accounts.append({
"account": company_account,
"bank_account": bank_transaction.bank_account,
"credit_in_account_currency": bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"debit_in_account_currency":bank_transaction.deposit
if bank_transaction.deposit > 0
else 0,
})
company = frappe.get_value("Account", company_account, "company")
journal_entry_dict = {
"voucher_type" : entry_type,
"company" : company,
"posting_date" : posting_date,
"cheque_date" : reference_date,
"cheque_no" : reference_number,
"mode_of_payment" : mode_of_payment
}
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.update(journal_entry_dict)
journal_entry.set("accounts", accounts)
if allow_edit:
return journal_entry
journal_entry.insert()
journal_entry.submit()
if bank_transaction.deposit > 0:
paid_amount = bank_transaction.deposit
else:
paid_amount = bank_transaction.withdrawal
vouchers = json.dumps([{
"payment_doctype":"Journal Entry",
"payment_name":journal_entry.name,
"amount":paid_amount}])
return reconcile_vouchers(bank_transaction.name, vouchers)
@frappe.whitelist()
def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None,
mode_of_payment=None, project=None, cost_center=None, allow_edit=None):
# Create a new payment entry based on the bank transaction
bank_transaction = frappe.db.get_values(
"Bank Transaction", bank_transaction_name,
fieldname=["name", "unallocated_amount", "deposit", "bank_account"] ,
as_dict=True
)[0]
paid_amount = bank_transaction.unallocated_amount
payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
company = frappe.get_value("Account", company_account, "company")
payment_entry_dict = {
"company" : company,
"payment_type" : payment_type,
"reference_no" : reference_number,
"reference_date" : reference_date,
"party_type" : party_type,
"party" : party,
"posting_date" : posting_date,
"paid_amount": paid_amount,
"received_amount": paid_amount
}
payment_entry = frappe.new_doc("Payment Entry")
payment_entry.update(payment_entry_dict)
if mode_of_payment:
payment_entry.mode_of_payment = mode_of_payment
if project:
payment_entry.project = project
if cost_center:
payment_entry.cost_center = cost_center
if payment_type == "Receive":
payment_entry.paid_to = company_account
else:
payment_entry.paid_from = company_account
payment_entry.validate()
if allow_edit:
return payment_entry
payment_entry.insert()
payment_entry.submit()
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment_entry.name,
"amount":paid_amount}])
return reconcile_vouchers(bank_transaction.name, vouchers)
@frappe.whitelist()
def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
for voucher in vouchers:
voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name'])
total_amount += get_paid_amount(frappe._dict({
'payment_document': voucher['payment_doctype'],
'payment_entry': voucher['payment_name'],
}), transaction.currency)
if total_amount > transaction.unallocated_amount:
frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
for voucher in vouchers:
gl_entry = frappe.db.get_value("GL Entry", dict(account=account, voucher_type=voucher['payment_doctype'], voucher_no=voucher['payment_name']), ['credit', 'debit'], as_dict=1)
gl_amount, transaction_amount = (gl_entry.credit, transaction.deposit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.withdrawal)
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
transaction.append("payment_entries", {
"payment_document": voucher['payment_entry'].doctype,
"payment_entry": voucher['payment_entry'].name,
"allocated_amount": allocated_amount
})
transaction.save()
transaction.update_allocations()
return frappe.get_doc("Bank Transaction", bank_transaction_name)
@frappe.whitelist()
def get_linked_payments(bank_transaction_name, document_types = None):
# get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_account = frappe.db.get_values(
"Bank Account",
transaction.bank_account,
["account", "company"],
as_dict=True)[0]
(account, company) = (bank_account.account, bank_account.company)
matching = check_matching(account, company, transaction, document_types)
return matching
def check_matching(bank_account, company, transaction, document_types):
# combine all types of vocuhers
subquery = get_queries(bank_account, company, transaction, document_types)
filters = {
"amount": transaction.unallocated_amount,
"payment_type" : "Receive" if transaction.deposit > 0 else "Pay",
"reference_no": transaction.reference_number,
"party_type": transaction.party_type,
"party": transaction.party,
"bank_account": bank_account
}
matching_vouchers = []
for query in subquery:
matching_vouchers.extend(
frappe.db.sql(query, filters,)
)
return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else []
def get_queries(bank_account, company, transaction, document_types):
# get queries to get matching vouchers
amount_condition = "=" if "exact_match" in document_types else "<="
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
queries = []
if "payment_entry" in document_types:
pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
queries.extend([pe_amount_matching])
if "journal_entry" in document_types:
je_amount_matching = get_je_matching_query(amount_condition, transaction)
queries.extend([je_amount_matching])
if transaction.deposit > 0 and "sales_invoice" in document_types:
si_amount_matching = get_si_matching_query(amount_condition)
queries.extend([si_amount_matching])
if transaction.withdrawal > 0:
if "purchase_invoice" in document_types:
pi_amount_matching = get_pi_matching_query(amount_condition)
queries.extend([pi_amount_matching])
if "expense_claim" in document_types:
ec_amount_matching = get_ec_matching_query(bank_account, company, amount_condition)
queries.extend([ec_amount_matching])
return queries
def get_pe_matching_query(amount_condition, account_from_to, transaction):
# get matching payment entries query
if transaction.deposit > 0:
currency_field = "paid_to_account_currency as currency"
else:
currency_field = "paid_from_account_currency as currency"
return f"""
SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Payment Entry' as doctype,
name,
paid_amount,
reference_no,
reference_date,
party,
party_type,
posting_date,
{currency_field}
FROM
`tabPayment Entry`
WHERE
paid_amount {amount_condition} %(amount)s
AND docstatus = 1
AND payment_type IN (%(payment_type)s, 'Internal Transfer')
AND ifnull(clearance_date, '') = ""
AND {account_from_to} = %(bank_account)s
"""
def get_je_matching_query(amount_condition, transaction):
# get matching journal entry query
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
return f"""
SELECT
(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
+ 1) AS rank ,
'Journal Entry' as doctype,
je.name,
jea.{cr_or_dr}_in_account_currency as paid_amount,
je.cheque_no as reference_no,
je.cheque_date as reference_date,
je.pay_to_recd_from as party,
jea.party_type,
je.posting_date,
jea.account_currency as currency
FROM
`tabJournal Entry Account` as jea
JOIN
`tabJournal Entry` as je
ON
jea.parent = je.name
WHERE
(je.clearance_date is null or je.clearance_date='0000-00-00')
AND jea.account = %(bank_account)s
AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
AND je.docstatus = 1
"""
def get_si_matching_query(amount_condition):
# get matchin sales invoice query
return f"""
SELECT
( CASE WHEN si.customer = %(party)s THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Sales Invoice' as doctype,
si.name,
sip.amount as paid_amount,
'' as reference_no,
'' as reference_date,
si.customer as party,
'Customer' as party_type,
si.posting_date,
si.currency
FROM
`tabSales Invoice Payment` as sip
JOIN
`tabSales Invoice` as si
ON
sip.parent = si.name
WHERE (sip.clearance_date is null or sip.clearance_date='0000-00-00')
AND sip.account = %(bank_account)s
AND sip.amount {amount_condition} %(amount)s
AND si.docstatus = 1
"""
def get_pi_matching_query(amount_condition):
# get matching purchase invoice query
return f"""
SELECT
( CASE WHEN supplier = %(party)s THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Purchase Invoice' as doctype,
name,
paid_amount,
'' as reference_no,
'' as reference_date,
supplier as party,
'Supplier' as party_type,
posting_date,
currency
FROM
`tabPurchase Invoice`
WHERE
paid_amount {amount_condition} %(amount)s
AND docstatus = 1
AND is_paid = 1
AND ifnull(clearance_date, '') = ""
AND cash_bank_account = %(bank_account)s
"""
def get_ec_matching_query(bank_account, company, amount_condition):
# get matching Expense Claim query
mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account",
filters={"default_account": bank_account}, fields=["parent"])]
mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
company_currency = get_company_currency(company)
return f"""
SELECT
( CASE WHEN employee = %(party)s THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Expense Claim' as doctype,
name,
total_sanctioned_amount as paid_amount,
'' as reference_no,
'' as reference_date,
employee as party,
'Employee' as party_type,
posting_date,
'{company_currency}' as currency
FROM
`tabExpense Claim`
WHERE
total_sanctioned_amount {amount_condition} %(amount)s
AND docstatus = 1
AND is_paid = 1
AND ifnull(clearance_date, '') = ""
AND mode_of_payment in {mode_of_payments}
"""

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestBankReconciliationTool(unittest.TestCase):
pass

View File

@ -0,0 +1,3 @@
.warnings .warning {
margin-bottom: 40px;
}

View File

@ -0,0 +1,574 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on("Bank Statement Import", {
setup(frm) {
frappe.realtime.on("data_import_refresh", ({ data_import }) => {
frm.import_in_progress = false;
if (data_import !== frm.doc.name) return;
frappe.model.clear_doc("Bank Statement Import", frm.doc.name);
frappe.model
.with_doc("Bank Statement Import", frm.doc.name)
.then(() => {
frm.refresh();
});
});
frappe.realtime.on("data_import_progress", (data) => {
frm.import_in_progress = true;
if (data.data_import !== frm.doc.name) {
return;
}
let percent = Math.floor((data.current * 100) / data.total);
let seconds = Math.floor(data.eta);
let minutes = Math.floor(data.eta / 60);
let eta_message =
// prettier-ignore
seconds < 60
? __('About {0} seconds remaining', [seconds])
: minutes === 1
? __('About {0} minute remaining', [minutes])
: __('About {0} minutes remaining', [minutes]);
let message;
if (data.success) {
let message_args = [data.current, data.total, eta_message];
message =
frm.doc.import_type === "Insert New Records"
? __("Importing {0} of {1}, {2}", message_args)
: __("Updating {0} of {1}, {2}", message_args);
}
if (data.skipping) {
message = __(
"Skipping {0} of {1}, {2}",
[
data.current,
data.total,
eta_message,
]
);
}
frm.dashboard.show_progress(
__("Import Progress"),
percent,
message
);
frm.page.set_indicator(__("In Progress"), "orange");
// hide progress when complete
if (data.current === data.total) {
setTimeout(() => {
frm.dashboard.hide();
frm.refresh();
}, 2000);
}
});
frm.set_query("reference_doctype", () => {
return {
filters: {
name: ["in", frappe.boot.user.can_import],
},
};
});
frm.get_field("import_file").df.options = {
restrictions: {
allowed_file_types: [".csv", ".xls", ".xlsx"],
},
};
frm.has_import_file = () => {
return frm.doc.import_file || frm.doc.google_sheets_url;
};
},
refresh(frm) {
frm.page.hide_icon_group();
frm.trigger("update_indicators");
frm.trigger("import_file");
frm.trigger("show_import_log");
frm.trigger("show_import_warnings");
frm.trigger("toggle_submit_after_import");
frm.trigger("show_import_status");
frm.trigger("show_report_error_button");
if (frm.doc.status === "Partial Success") {
frm.add_custom_button(__("Export Errored Rows"), () =>
frm.trigger("export_errored_rows")
);
}
if (frm.doc.status.includes("Success")) {
frm.add_custom_button(
__("Go to {0} List", [frm.doc.reference_doctype]),
() => frappe.set_route("List", frm.doc.reference_doctype)
);
}
},
onload_post_render(frm) {
frm.trigger("update_primary_action");
},
update_primary_action(frm) {
if (frm.is_dirty()) {
frm.enable_save();
return;
}
frm.disable_save();
if (frm.doc.status !== "Success") {
if (!frm.is_new() && frm.has_import_file()) {
let label =
frm.doc.status === "Pending"
? __("Start Import")
: __("Retry");
frm.page.set_primary_action(label, () =>
frm.events.start_import(frm)
);
} else {
frm.page.set_primary_action(__("Save"), () => frm.save());
}
}
},
update_indicators(frm) {
const indicator = frappe.get_indicator(frm.doc);
if (indicator) {
frm.page.set_indicator(indicator[0], indicator[1]);
} else {
frm.page.clear_indicator();
}
},
show_import_status(frm) {
let import_log = JSON.parse(frm.doc.import_log || "[]");
let successful_records = import_log.filter((log) => log.success);
let failed_records = import_log.filter((log) => !log.success);
if (successful_records.length === 0) return;
let message;
if (failed_records.length === 0) {
let message_args = [successful_records.length];
if (frm.doc.import_type === "Insert New Records") {
message =
successful_records.length > 1
? __("Successfully imported {0} records.", message_args)
: __("Successfully imported {0} record.", message_args);
} else {
message =
successful_records.length > 1
? __("Successfully updated {0} records.", message_args)
: __("Successfully updated {0} record.", message_args);
}
} else {
let message_args = [successful_records.length, import_log.length];
if (frm.doc.import_type === "Insert New Records") {
message =
successful_records.length > 1
? __(
"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
)
: __(
"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
);
} else {
message =
successful_records.length > 1
? __(
"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
)
: __(
"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
);
}
}
frm.dashboard.set_headline(message);
},
show_report_error_button(frm) {
if (frm.doc.status === "Error") {
frappe.db
.get_list("Error Log", {
filters: { method: frm.doc.name },
fields: ["method", "error"],
order_by: "creation desc",
limit: 1,
})
.then((result) => {
if (result.length > 0) {
frm.add_custom_button("Report Error", () => {
let fake_xhr = {
responseText: JSON.stringify({
exc: result[0].error,
}),
};
frappe.request.report_error(fake_xhr, {});
});
}
});
}
},
start_import(frm) {
frm.call({
method: "form_start_import",
args: { data_import: frm.doc.name },
btn: frm.page.btn_primary,
}).then((r) => {
if (r.message === true) {
frm.disable_save();
}
});
},
download_template() {
let method =
"/api/method/frappe.core.doctype.data_import.data_import.download_template";
open_url_post(method, {
doctype: "Bank Transaction",
export_records: "5_records",
export_fields: {
"Bank Transaction": [
"date",
"deposit",
"withdrawal",
"description",
"reference_number",
],
},
});
},
reference_doctype(frm) {
frm.trigger("toggle_submit_after_import");
},
toggle_submit_after_import(frm) {
frm.toggle_display("submit_after_import", false);
let doctype = frm.doc.reference_doctype;
if (doctype) {
frappe.model.with_doctype(doctype, () => {
let meta = frappe.get_meta(doctype);
frm.toggle_display("submit_after_import", meta.is_submittable);
});
}
},
google_sheets_url(frm) {
if (!frm.is_dirty()) {
frm.trigger("import_file");
} else {
frm.trigger("update_primary_action");
}
},
refresh_google_sheet(frm) {
frm.trigger("import_file");
},
import_file(frm) {
frm.toggle_display("section_import_preview", frm.has_import_file());
if (!frm.has_import_file()) {
frm.get_field("import_preview").$wrapper.empty();
return;
} else {
frm.trigger("update_primary_action");
}
// load import preview
frm.get_field("import_preview").$wrapper.empty();
$('<span class="text-muted">')
.html(__("Loading import file..."))
.appendTo(frm.get_field("import_preview").$wrapper);
frm.call({
method: "get_preview_from_template",
args: {
data_import: frm.doc.name,
import_file: frm.doc.import_file,
google_sheets_url: frm.doc.google_sheets_url,
},
error_handlers: {
TimestampMismatchError() {
// ignore this error
},
},
}).then((r) => {
let preview_data = r.message;
frm.events.show_import_preview(frm, preview_data);
frm.events.show_import_warnings(frm, preview_data);
});
},
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
show_import_preview(frm, preview_data) {
let import_log = JSON.parse(frm.doc.import_log || "[]");
if (
frm.import_preview &&
frm.import_preview.doctype === frm.doc.reference_doctype
) {
frm.import_preview.preview_data = preview_data;
frm.import_preview.import_log = import_log;
frm.import_preview.refresh();
return;
}
frappe.require("/assets/js/data_import_tools.min.js", () => {
frm.import_preview = new frappe.data_import.ImportPreview({
wrapper: frm.get_field("import_preview").$wrapper,
doctype: frm.doc.reference_doctype,
preview_data,
import_log,
frm,
events: {
remap_column(changed_map) {
let template_options = JSON.parse(
frm.doc.template_options || "{}"
);
template_options.column_to_field_map =
template_options.column_to_field_map || {};
Object.assign(
template_options.column_to_field_map,
changed_map
);
frm.set_value(
"template_options",
JSON.stringify(template_options)
);
frm.save().then(() => frm.trigger("import_file"));
},
},
});
});
},
export_errored_rows(frm) {
open_url_post(
"/api/method/frappe.core.doctype.data_import.data_import.download_errored_template",
{
data_import_name: frm.doc.name,
}
);
},
show_import_warnings(frm, preview_data) {
let columns = preview_data.columns;
let warnings = JSON.parse(frm.doc.template_warnings || "[]");
warnings = warnings.concat(preview_data.warnings || []);
frm.toggle_display("import_warnings_section", warnings.length > 0);
if (warnings.length === 0) {
frm.get_field("import_warnings").$wrapper.html("");
return;
}
// group warnings by row
let warnings_by_row = {};
let other_warnings = [];
for (let warning of warnings) {
if (warning.row) {
warnings_by_row[warning.row] =
warnings_by_row[warning.row] || [];
warnings_by_row[warning.row].push(warning);
} else {
other_warnings.push(warning);
}
}
let html = "";
html += Object.keys(warnings_by_row)
.map((row_number) => {
let message = warnings_by_row[row_number]
.map((w) => {
if (w.field) {
let label =
w.field.label +
(w.field.parent !== frm.doc.reference_doctype
? ` (${w.field.parent})`
: "");
return `<li>${label}: ${w.message}</li>`;
}
return `<li>${w.message}</li>`;
})
.join("");
return `
<div class="warning" data-row="${row_number}">
<h5 class="text-uppercase">${__("Row {0}", [row_number])}</h5>
<div class="body"><ul>${message}</ul></div>
</div>
`;
})
.join("");
html += other_warnings
.map((warning) => {
let header = "";
if (warning.col) {
let column_number = `<span class="text-uppercase">${__(
"Column {0}",
[warning.col]
)}</span>`;
let column_header = columns[warning.col].header_title;
header = `${column_number} (${column_header})`;
}
return `
<div class="warning" data-col="${warning.col}">
<h5>${header}</h5>
<div class="body">${warning.message}</div>
</div>
`;
})
.join("");
frm.get_field("import_warnings").$wrapper.html(`
<div class="row">
<div class="col-sm-10 warnings">${html}</div>
</div>
`);
},
show_failed_logs(frm) {
frm.trigger("show_import_log");
},
show_import_log(frm) {
let import_log = JSON.parse(frm.doc.import_log || "[]");
let logs = import_log;
frm.toggle_display("import_log", false);
frm.toggle_display("import_log_section", logs.length > 0);
if (logs.length === 0) {
frm.get_field("import_log_preview").$wrapper.empty();
return;
}
let rows = logs
.map((log) => {
let html = "";
if (log.success) {
if (frm.doc.import_type === "Insert New Records") {
html = __(
"Successfully imported {0}", [
`<span class="underline">${frappe.utils.get_form_link(
frm.doc.reference_doctype,
log.docname,
true
)}<span>`,
]
);
} else {
html = __(
"Successfully updated {0}", [
`<span class="underline">${frappe.utils.get_form_link(
frm.doc.reference_doctype,
log.docname,
true
)}<span>`,
]
);
}
} else {
let messages = log.messages
.map(JSON.parse)
.map((m) => {
let title = m.title
? `<strong>${m.title}</strong>`
: "";
let message = m.message
? `<div>${m.message}</div>`
: "";
return title + message;
})
.join("");
let id = frappe.dom.get_unique_id();
html = `${messages}
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false" aria-controls="${id}" style="margin-top: 15px;">
${__("Show Traceback")}
</button>
<div class="collapse" id="${id}" style="margin-top: 15px;">
<div class="well">
<pre>${log.exception}</pre>
</div>
</div>`;
}
let indicator_color = log.success ? "green" : "red";
let title = log.success ? __("Success") : __("Failure");
if (frm.doc.show_failed_logs && log.success) {
return "";
}
return `<tr>
<td>${log.row_indexes.join(", ")}</td>
<td>
<div class="indicator ${indicator_color}">${title}</div>
</td>
<td>
${html}
</td>
</tr>`;
})
.join("");
if (!rows && frm.doc.show_failed_logs) {
rows = `<tr><td class="text-center text-muted" colspan=3>
${__("No failed logs")}
</td></tr>`;
}
frm.get_field("import_log_preview").$wrapper.html(`
<table class="table table-bordered">
<tr class="text-muted">
<th width="10%">${__("Row Number")}</th>
<th width="10%">${__("Status")}</th>
<th width="80%">${__("Message")}</th>
</tr>
${rows}
</table>
`);
},
show_missing_link_values(frm, missing_link_values) {
let can_be_created_automatically = missing_link_values.every(
(d) => d.has_one_mandatory_field
);
let html = missing_link_values
.map((d) => {
let doctype = d.doctype;
let values = d.missing_values;
return `
<h5>${doctype}</h5>
<ul>${values.map((v) => `<li>${v}</li>`).join("")}</ul>
`;
})
.join("");
if (can_be_created_automatically) {
// prettier-ignore
let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?');
frappe.confirm(message + html, () => {
frm.call("create_missing_link_values", {
missing_link_values,
}).then((r) => {
let records = r.message;
frappe.msgprint(__(
"Created {0} records successfully.", [
records.length,
]
));
});
});
} else {
frappe.msgprint(
// prettier-ignore
__('The following records needs to be created before we can import your file.') + html
);
}
},
});

View File

@ -0,0 +1,227 @@
{
"actions": [],
"autoname": "format:Bank Statement Import on {creation}",
"beta": 1,
"creation": "2019-08-04 14:16:08.318714",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"bank_account",
"bank",
"column_break_4",
"google_sheets_url",
"refresh_google_sheet",
"html_5",
"import_file",
"download_template",
"status",
"template_options",
"import_warnings_section",
"template_warnings",
"import_warnings",
"section_import_preview",
"import_preview",
"import_log_section",
"import_log",
"show_failed_logs",
"import_log_preview",
"reference_doctype",
"import_type",
"submit_after_import",
"mute_emails"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"reqd": 1,
"set_only_once": 1
},
{
"fieldname": "bank_account",
"fieldtype": "Link",
"label": "Bank Account",
"options": "Bank Account",
"reqd": 1,
"set_only_once": 1
},
{
"depends_on": "eval:doc.bank_account",
"fetch_from": "bank_account.bank",
"fieldname": "bank",
"fieldtype": "Link",
"label": "Bank",
"options": "Bank",
"read_only": 1,
"set_only_once": 1
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "download_template",
"fieldtype": "Button",
"label": "Download Template"
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "import_file",
"fieldtype": "Attach",
"in_list_view": 1,
"label": "Import File"
},
{
"fieldname": "import_preview",
"fieldtype": "HTML",
"label": "Import Preview"
},
{
"fieldname": "section_import_preview",
"fieldtype": "Section Break",
"label": "Preview"
},
{
"fieldname": "template_options",
"fieldtype": "Code",
"hidden": 1,
"label": "Template Options",
"options": "JSON",
"read_only": 1
},
{
"fieldname": "import_log",
"fieldtype": "Code",
"label": "Import Log",
"options": "JSON"
},
{
"fieldname": "import_log_section",
"fieldtype": "Section Break",
"label": "Import Log"
},
{
"fieldname": "import_log_preview",
"fieldtype": "HTML",
"label": "Import Log Preview"
},
{
"default": "Pending",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 1,
"label": "Status",
"options": "Pending\nSuccess\nPartial Success\nError",
"read_only": 1
},
{
"fieldname": "template_warnings",
"fieldtype": "Code",
"hidden": 1,
"label": "Template Warnings",
"options": "JSON"
},
{
"fieldname": "import_warnings_section",
"fieldtype": "Section Break",
"label": "Import File Errors and Warnings"
},
{
"fieldname": "import_warnings",
"fieldtype": "HTML",
"label": "Import Warnings"
},
{
"default": "0",
"fieldname": "show_failed_logs",
"fieldtype": "Check",
"label": "Show Failed Logs"
},
{
"depends_on": "eval:!doc.__islocal && !doc.import_file",
"fieldname": "html_5",
"fieldtype": "HTML",
"options": "<h5 class=\"text-muted uppercase\">Or</h5>"
},
{
"depends_on": "eval:!doc.__islocal && !doc.import_file\n",
"description": "Must be a publicly accessible Google Sheets URL",
"fieldname": "google_sheets_url",
"fieldtype": "Data",
"label": "Import from Google Sheets"
},
{
"depends_on": "eval:doc.google_sheets_url && !doc.__unsaved",
"fieldname": "refresh_google_sheet",
"fieldtype": "Button",
"label": "Refresh Google Sheet"
},
{
"default": "Bank Transaction",
"fieldname": "reference_doctype",
"fieldtype": "Link",
"hidden": 1,
"in_list_view": 1,
"label": "Document Type",
"options": "DocType",
"reqd": 1,
"set_only_once": 1
},
{
"default": "Insert New Records",
"fieldname": "import_type",
"fieldtype": "Select",
"hidden": 1,
"in_list_view": 1,
"label": "Import Type",
"options": "\nInsert New Records\nUpdate Existing Records",
"reqd": 1,
"set_only_once": 1
},
{
"default": "1",
"fieldname": "submit_after_import",
"fieldtype": "Check",
"hidden": 1,
"label": "Submit After Import",
"set_only_once": 1
},
{
"default": "1",
"fieldname": "mute_emails",
"fieldtype": "Check",
"hidden": 1,
"label": "Don't Send Emails",
"set_only_once": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
}
],
"hide_toolbar": 1,
"links": [],
"modified": "2021-02-10 19:29:59.027325",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Import",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,205 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import csv
import json
import re
import openpyxl
from openpyxl.styles import Font
from openpyxl.utils import get_column_letter
from six import string_types
import frappe
from frappe.core.doctype.data_import.importer import Importer, ImportFile
from frappe.utils.background_jobs import enqueue
from frappe.utils.xlsxutils import handle_html, ILLEGAL_CHARACTERS_RE
from frappe import _
from frappe.core.doctype.data_import.data_import import DataImport
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
super(BankStatementImport, self).__init__(*args, **kwargs)
def validate(self):
doc_before_save = self.get_doc_before_save()
if (
not (self.import_file or self.google_sheets_url)
or (doc_before_save and doc_before_save.import_file != self.import_file)
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
):
template_options_dict = {}
column_to_field_map = {}
bank = frappe.get_doc("Bank", self.bank)
for i in bank.bank_transaction_mapping:
column_to_field_map[i.file_field] = i.bank_transaction_field
template_options_dict["column_to_field_map"] = column_to_field_map
self.template_options = json.dumps(template_options_dict)
self.template_warnings = ""
self.validate_import_file()
self.validate_google_sheets_url()
def start_import(self):
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.utils.scheduler import is_scheduler_inactive
if is_scheduler_inactive() and not frappe.flags.in_test:
frappe.throw(
_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")
)
enqueued_jobs = [d.get("job_name") for d in get_info()]
if self.name not in enqueued_jobs:
enqueue(
start_import,
queue="default",
timeout=6000,
event="data_import",
job_name=self.name,
data_import=self.name,
bank_account=self.bank_account,
import_file_path=self.import_file,
bank=self.bank,
template_options=self.template_options,
now=frappe.conf.developer_mode or frappe.flags.in_test,
)
return True
return False
@frappe.whitelist()
def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
import_file, google_sheets_url
)
@frappe.whitelist()
def form_start_import(data_import):
return frappe.get_doc("Bank Statement Import", data_import).start_import()
@frappe.whitelist()
def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows()
def start_import(data_import, bank_account, import_file_path, bank, template_options):
"""This method runs in background job"""
update_mapping_db(bank, template_options)
data_import = frappe.get_doc("Bank Statement Import", data_import)
import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records")
data = import_file.raw_data
add_bank_account(data, bank_account)
write_files(import_file, data)
try:
i = Importer(data_import.reference_doctype, data_import=data_import)
i.import_data()
except Exception:
frappe.db.rollback()
data_import.db_set("status", "Error")
frappe.log_error(title=data_import.name)
finally:
frappe.flags.in_import = False
frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name})
def update_mapping_db(bank, template_options):
bank = frappe.get_doc("Bank", bank)
for d in bank.bank_transaction_mapping:
d.delete()
for d in json.loads(template_options)["column_to_field_map"].items():
bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1] ,"file_field": d[0]} )
bank.save()
def add_bank_account(data, bank_account):
bank_account_loc = None
if "Bank Account" not in data[0]:
data[0].append("Bank Account")
else:
for loc, header in enumerate(data[0]):
if header == "Bank Account":
bank_account_loc = loc
for row in data[1:]:
if bank_account_loc:
row[bank_account_loc] = bank_account
else:
row.append(bank_account)
def write_files(import_file, data):
full_file_path = import_file.file_doc.get_full_path()
parts = import_file.file_doc.get_extension()
extension = parts[1]
extension = extension.lstrip(".")
if extension == "csv":
with open(full_file_path, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerows(data)
elif extension == "xlsx" or "xls":
write_xlsx(data, "trans", file_path = full_file_path)
def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
# from xlsx utils with changes
column_widths = column_widths or []
if wb is None:
wb = openpyxl.Workbook(write_only=True)
ws = wb.create_sheet(sheet_name, 0)
for i, column_width in enumerate(column_widths):
if column_width:
ws.column_dimensions[get_column_letter(i + 1)].width = column_width
row1 = ws.row_dimensions[1]
row1.font = Font(name='Calibri', bold=True)
for row in data:
clean_row = []
for item in row:
if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']):
value = handle_html(item)
else:
value = item
if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
# Remove illegal characters from the string
value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
clean_row.append(value)
ws.append(clean_row)
wb.save(file_path)
return True
@frappe.whitelist()
def upload_bank_statement(**args):
args = frappe._dict(args)
bsi = frappe.new_doc("Bank Statement Import")
if args.company:
bsi.update({
"company": args.company,
})
if args.bank_account:
bsi.update({
"bank_account": args.bank_account
})
return bsi

View File

@ -0,0 +1,36 @@
let imports_in_progress = [];
frappe.listview_settings['Bank Statement Import'] = {
onload(listview) {
frappe.realtime.on('data_import_progress', data => {
if (!imports_in_progress.includes(data.data_import)) {
imports_in_progress.push(data.data_import);
}
});
frappe.realtime.on('data_import_refresh', data => {
imports_in_progress = imports_in_progress.filter(
d => d !== data.data_import
);
listview.refresh();
});
},
get_indicator: function(doc) {
var colors = {
'Pending': 'orange',
'Not Started': 'orange',
'Partial Success': 'orange',
'Success': 'green',
'In Progress': 'orange',
'Error': 'red'
};
let status = doc.status;
if (imports_in_progress.includes(doc.name)) {
status = 'In Progress';
}
if (status == 'Pending') {
status = 'Not Started';
}
return [__(status), colors[status], 'status,=,' + doc.status];
},
hide_name_column: true
};

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestBankStatementImport(unittest.TestCase):
pass

View File

@ -1,8 +0,0 @@
// Copyright (c) 2017, sathishpy@gmail.com and contributors
// For license information, please see license.txt
frappe.ui.form.on('Bank Statement Settings', {
refresh: function(frm) {
}
});

View File

@ -1,272 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"beta": 0,
"creation": "2017-11-13 13:38:10.863592",
"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": "bank",
"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": "Bank Account",
"length": 0,
"no_copy": 0,
"options": "Bank",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "'%d/%m/%Y'",
"fieldname": "date_format",
"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": "Date 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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "statement_header_mapping",
"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": "Statement Header Mapping",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "header_items",
"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": "Statement Headers",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Settings Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "transaction_data_mapping",
"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": "Transaction Data Mapping",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "mapped_items",
"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": "Mapped Items",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Transaction Settings Item",
"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,
"translatable": 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-04-07 18:57:04.048423",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Settings",
"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": "Accounts 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
}

View File

@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class BankStatementSettings(Document):
def autoname(self):
self.name = self.bank + "-Statement-Settings"

View File

@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Bank Statement Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Bank Statement Settings
() => frappe.tests.make('Bank Statement Settings', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestBankStatementSettings(unittest.TestCase):
pass

View File

@ -1,101 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-01-08 00:16:42.762980",
"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": "mapped_header",
"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": "Mapped Header",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "stmt_header",
"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": "Bank 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": 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-01-08 00:19:14.841134",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Settings Item",
"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
}

View File

@ -1,100 +0,0 @@
// Copyright (c) 2017, sathishpy@gmail.com and contributors
// For license information, please see license.txt
frappe.ui.form.on('Bank Statement Transaction Entry', {
setup: function(frm) {
frm.events.account_filters(frm)
frm.events.invoice_filter(frm)
},
refresh: function(frm) {
frm.set_df_property("bank_account", "read_only", frm.doc.__islocal ? 0 : 1);
frm.set_df_property("from_date", "read_only", frm.doc.__islocal ? 0 : 1);
frm.set_df_property("to_date", "read_only", frm.doc.__islocal ? 0 : 1);
},
invoke_doc_function(frm, method) {
frappe.call({
doc: frm.doc,
method: method,
callback: function(r) {
if(!r.exe) {
frm.refresh_fields();
}
}
});
},
account_filters: function(frm) {
frm.fields_dict['bank_account'].get_query = function(doc, dt, dn) {
return {
filters:[
["Account", "account_type", "in", ["Bank"]]
]
}
};
frm.fields_dict['receivable_account'].get_query = function(doc, dt, dn) {
return {
filters: {"account_type": "Receivable"}
}
};
frm.fields_dict['payable_account'].get_query = function(doc, dt, dn) {
return {
filters: {"account_type": "Payable"}
}
};
},
invoice_filter: function(frm) {
frm.set_query("invoice", "payment_invoice_items", function(doc, cdt, cdn) {
let row = locals[cdt][cdn]
if (row.party_type == "Customer") {
return {
filters:[[row.invoice_type, "customer", "in", [row.party]],
[row.invoice_type, "status", "!=", "Cancelled" ],
[row.invoice_type, "posting_date", "<", row.transaction_date ],
[row.invoice_type, "outstanding_amount", ">", 0 ]]
}
} else if (row.party_type == "Supplier") {
return {
filters:[[row.invoice_type, "supplier", "in", [row.party]],
[row.invoice_type, "status", "!=", "Cancelled" ],
[row.invoice_type, "posting_date", "<", row.transaction_date ],
[row.invoice_type, "outstanding_amount", ">", 0 ]]
}
}
});
},
match_invoices: function(frm) {
frm.events.invoke_doc_function(frm, "populate_matching_invoices");
},
create_payments: function(frm) {
frm.events.invoke_doc_function(frm, "create_payment_entries");
},
submit_payments: function(frm) {
frm.events.invoke_doc_function(frm, "submit_payment_entries");
},
});
frappe.ui.form.on('Bank Statement Transaction Invoice Item', {
party_type: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (row.party_type == "Customer") {
row.invoice_type = "Sales Invoice";
} else if (row.party_type == "Supplier") {
row.invoice_type = "Purchase Invoice";
} else if (row.party_type == "Account") {
row.invoice_type = "Journal Entry";
}
refresh_field("invoice_type", row.name, "payment_invoice_items");
},
invoice_type: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (row.invoice_type == "Purchase Invoice") {
row.party_type = "Supplier";
} else if (row.invoice_type == "Sales Invoice") {
row.party_type = "Customer";
}
refresh_field("party_type", row.name, "payment_invoice_items");
}
});

View File

@ -1,792 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"beta": 0,
"creation": "2017-11-07 13:48:13.123185",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_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": "Bank 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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"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": "From Date",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"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": "To Date",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_settings",
"fieldtype": "Link",
"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": "Bank Statement Settings",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Settings",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank",
"fieldtype": "Link",
"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": "Bank",
"length": 0,
"no_copy": 0,
"options": "Bank",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "receivable_account",
"fieldtype": "Link",
"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": "Receivable 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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payable_account",
"fieldtype": "Link",
"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": "Payable 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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_statement",
"fieldtype": "Attach",
"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": "Bank Statement",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "section_break_6",
"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": "Bank Transaction Entries",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "new_transaction_items",
"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": "New Transactions",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Transaction Payment Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length",
"fieldname": "section_break_9",
"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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "match_invoices",
"fieldtype": "Button",
"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": "Match Transaction to Invoices",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_14",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "create_payments",
"fieldtype": "Button",
"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": "Create New Payment/Journal Entry",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_16",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "submit_payments",
"fieldtype": "Button",
"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": "Submit/Reconcile Payments",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.new_transaction_items && doc.new_transaction_items.length",
"fieldname": "section_break_18",
"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": "Matching Invoices",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_invoice_items",
"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": "Payment Invoice Items",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Transaction Invoice Item",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reconciled_transactions",
"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": "Reconciled Transactions",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reconciled_transaction_items",
"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": "Reconciled Transactions",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Transaction Payment Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Bank Statement Transaction Entry",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-09-14 18:04:44.170455",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Transaction Entry",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"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": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"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
}

View File

@ -1,443 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from erpnext.accounts.utils import get_outstanding_invoices
from frappe.utils import nowdate
from datetime import datetime
import csv, os, re, io
import difflib
import copy
class BankStatementTransactionEntry(Document):
def autoname(self):
self.name = self.bank_account + "-" + self.from_date + "-" + self.to_date
if self.bank:
mapper_name = self.bank + "-Statement-Settings"
if not frappe.db.exists("Bank Statement Settings", mapper_name):
self.create_settings(self.bank)
self.bank_settings = mapper_name
def create_settings(self, bank):
mapper = frappe.new_doc("Bank Statement Settings")
mapper.bank = bank
mapper.date_format = "%Y-%m-%d"
mapper.bank_account = self.bank_account
for header in ["Date", "Particulars", "Withdrawals", "Deposits", "Balance"]:
header_item = mapper.append("header_items", {})
header_item.mapped_header = header_item.stmt_header = header
mapper.save()
def on_update(self):
if (not self.bank_statement):
self.reconciled_transaction_items = self.new_transaction_items = []
return
if len(self.new_transaction_items + self.reconciled_transaction_items) == 0:
self.populate_payment_entries()
else:
self.match_invoice_to_payment()
def validate(self):
if not self.new_transaction_items:
self.populate_payment_entries()
def get_statement_headers(self):
if not self.bank_settings:
frappe.throw(_("Bank Data mapper doesn't exist"))
mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings)
headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items}
return headers
def populate_payment_entries(self):
if self.bank_statement is None: return
file_url = self.bank_statement
if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
frappe.throw(_("Transactions already retreived from the statement"))
date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format")
if (date_format is None):
date_format = '%Y-%m-%d'
if self.bank_settings:
mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
statement_headers = self.get_statement_headers()
transactions = get_transaction_entries(file_url, statement_headers)
for entry in transactions:
date = entry[statement_headers["Date"]].strip()
#print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"]))
if (not date): continue
transaction_date = datetime.strptime(date, date_format).date()
if (self.from_date and transaction_date < datetime.strptime(self.from_date, '%Y-%m-%d').date()): continue
if (self.to_date and transaction_date > datetime.strptime(self.to_date, '%Y-%m-%d').date()): continue
bank_entry = self.append('new_transaction_items', {})
bank_entry.transaction_date = transaction_date
bank_entry.description = entry[statement_headers["Particulars"]]
mapped_item = next((entry for entry in mapped_items if entry.mapping_type == "Transaction" and frappe.safe_decode(entry.bank_data.lower()) in frappe.safe_decode(bank_entry.description.lower())), None)
if (mapped_item is not None):
bank_entry.party_type = mapped_item.mapped_data_type
bank_entry.party = mapped_item.mapped_data
else:
bank_entry.party_type = "Supplier" if not entry[statement_headers["Deposits"]].strip() else "Customer"
party_list = frappe.get_all(bank_entry.party_type, fields=["name"])
parties = [party.name for party in party_list]
matches = difflib.get_close_matches(frappe.safe_decode(bank_entry.description.lower()), parties, 1, 0.4)
if len(matches) > 0: bank_entry.party = matches[0]
bank_entry.amount = -float(entry[statement_headers["Withdrawals"]]) if not entry[statement_headers["Deposits"]].strip() else float(entry[statement_headers["Deposits"]])
self.map_unknown_transactions()
self.map_transactions_on_journal_entry()
def map_transactions_on_journal_entry(self):
for entry in self.new_transaction_items:
vouchers = frappe.db.sql("""select name, posting_date from `tabJournal Entry`
where posting_date='{0}' and total_credit={1} and cheque_no='{2}' and docstatus != 2
""".format(entry.transaction_date, abs(entry.amount), frappe.safe_decode(entry.description)), as_dict=True)
if (len(vouchers) == 1):
entry.reference_name = vouchers[0].name
def populate_matching_invoices(self):
self.payment_invoice_items = []
self.map_unknown_transactions()
added_invoices = []
for entry in self.new_transaction_items:
if (not entry.party or entry.party_type == "Account"): continue
account = self.receivable_account if entry.party_type == "Customer" else self.payable_account
invoices = get_outstanding_invoices(entry.party_type, entry.party, account)
transaction_date = datetime.strptime(entry.transaction_date, "%Y-%m-%d").date()
outstanding_invoices = [invoice for invoice in invoices if invoice.posting_date <= transaction_date]
amount = abs(entry.amount)
matching_invoices = [invoice for invoice in outstanding_invoices if invoice.outstanding_amount == amount]
sorted(outstanding_invoices, key=lambda k: k['posting_date'])
for e in (matching_invoices + outstanding_invoices):
added = next((inv for inv in added_invoices if inv == e.get('voucher_no')), None)
if (added is not None): continue
ent = self.append('payment_invoice_items', {})
ent.transaction_date = entry.transaction_date
ent.payment_description = frappe.safe_decode(entry.description)
ent.party_type = entry.party_type
ent.party = entry.party
ent.invoice = e.get('voucher_no')
added_invoices += [ent.invoice]
ent.invoice_type = "Sales Invoice" if entry.party_type == "Customer" else "Purchase Invoice"
ent.invoice_date = e.get('posting_date')
ent.outstanding_amount = e.get('outstanding_amount')
ent.allocated_amount = min(float(e.get('outstanding_amount')), amount)
amount -= float(e.get('outstanding_amount'))
if (amount <= 5): break
self.match_invoice_to_payment()
self.populate_matching_vouchers()
self.map_transactions_on_journal_entry()
def match_invoice_to_payment(self):
added_payments = []
for entry in self.new_transaction_items:
if (not entry.party or entry.party_type == "Account"): continue
entry.account = self.receivable_account if entry.party_type == "Customer" else self.payable_account
amount = abs(entry.amount)
payment, matching_invoices = None, []
for inv_entry in self.payment_invoice_items:
if (inv_entry.payment_description != frappe.safe_decode(entry.description) or inv_entry.transaction_date != entry.transaction_date): continue
if (inv_entry.party != entry.party): continue
matching_invoices += [inv_entry.invoice_type + "|" + inv_entry.invoice]
payment = get_payments_matching_invoice(inv_entry.invoice, entry.amount, entry.transaction_date)
doc = frappe.get_doc(inv_entry.invoice_type, inv_entry.invoice)
inv_entry.invoice_date = doc.posting_date
inv_entry.outstanding_amount = doc.outstanding_amount
inv_entry.allocated_amount = min(float(doc.outstanding_amount), amount)
amount -= inv_entry.allocated_amount
if (amount < 0): break
amount = abs(entry.amount)
if (payment is None):
order_doctype = "Sales Order" if entry.party_type=="Customer" else "Purchase Order"
from erpnext.controllers.accounts_controller import get_advance_payment_entries
payment_entries = get_advance_payment_entries(entry.party_type, entry.party, entry.account, order_doctype, against_all_orders=True)
payment_entries += self.get_matching_payments(entry.party, amount, entry.transaction_date)
payment = next((payment for payment in payment_entries if payment.amount == amount and payment not in added_payments), None)
if (payment is None):
print("Failed to find payments for {0}:{1}".format(entry.party, amount))
continue
added_payments += [payment]
entry.reference_type = payment.reference_type
entry.reference_name = payment.reference_name
entry.mode_of_payment = "Wire Transfer"
entry.outstanding_amount = min(amount, 0)
if (entry.payment_reference is None):
entry.payment_reference = frappe.safe_decode(entry.description)
entry.invoices = ",".join(matching_invoices)
#print("Matching payment is {0}:{1}".format(entry.reference_type, entry.reference_name))
def get_matching_payments(self, party, amount, pay_date):
query = """select 'Payment Entry' as reference_type, name as reference_name, paid_amount as amount
from `tabPayment Entry` where party='{0}' and paid_amount={1} and posting_date='{2}' and docstatus != 2
""".format(party, amount, pay_date)
matching_payments = frappe.db.sql(query, as_dict=True)
return matching_payments
def map_unknown_transactions(self):
for entry in self.new_transaction_items:
if (entry.party): continue
inv_type = "Sales Invoice" if (entry.amount > 0) else "Purchase Invoice"
party_type = "customer" if (entry.amount > 0) else "supplier"
query = """select posting_date, name, {0}, outstanding_amount
from `tab{1}` where ROUND(outstanding_amount)={2} and posting_date < '{3}'
""".format(party_type, inv_type, round(abs(entry.amount)), entry.transaction_date)
invoices = frappe.db.sql(query, as_dict = True)
if(len(invoices) > 0):
entry.party = invoices[0].get(party_type)
def populate_matching_vouchers(self):
for entry in self.new_transaction_items:
if (not entry.party or entry.reference_name): continue
print("Finding matching voucher for {0}".format(frappe.safe_decode(entry.description)))
amount = abs(entry.amount)
invoices = []
vouchers = get_matching_journal_entries(self.from_date, self.to_date, entry.party, self.bank_account, amount)
if len(vouchers) == 0: continue
for voucher in vouchers:
added = next((entry.invoice for entry in self.payment_invoice_items if entry.invoice == voucher.voucher_no), None)
if (added):
print("Found voucher {0}".format(added))
continue
print("Adding voucher {0} {1} {2}".format(voucher.voucher_no, voucher.posting_date, voucher.debit))
ent = self.append('payment_invoice_items', {})
ent.invoice_date = voucher.posting_date
ent.invoice_type = "Journal Entry"
ent.invoice = voucher.voucher_no
ent.payment_description = frappe.safe_decode(entry.description)
ent.allocated_amount = max(voucher.debit, voucher.credit)
invoices += [ent.invoice_type + "|" + ent.invoice]
entry.reference_type = "Journal Entry"
entry.mode_of_payment = "Wire Transfer"
entry.reference_name = ent.invoice
#entry.account = entry.party
entry.invoices = ",".join(invoices)
break
def create_payment_entries(self):
for payment_entry in self.new_transaction_items:
if (not payment_entry.party): continue
if (payment_entry.reference_name): continue
print("Creating payment entry for {0}".format(frappe.safe_decode(payment_entry.description)))
if (payment_entry.party_type == "Account"):
payment = self.create_journal_entry(payment_entry)
invoices = [payment.doctype + "|" + payment.name]
payment_entry.invoices = ",".join(invoices)
else:
payment = self.create_payment_entry(payment_entry)
invoices = [entry.reference_doctype + "|" + entry.reference_name for entry in payment.references if entry is not None]
payment_entry.invoices = ",".join(invoices)
payment_entry.mode_of_payment = payment.mode_of_payment
payment_entry.account = self.receivable_account if payment_entry.party_type == "Customer" else self.payable_account
payment_entry.reference_name = payment.name
payment_entry.reference_type = payment.doctype
frappe.msgprint(_("Successfully created payment entries"))
def create_payment_entry(self, pe):
payment = frappe.new_doc("Payment Entry")
payment.posting_date = pe.transaction_date
payment.payment_type = "Receive" if pe.party_type == "Customer" else "Pay"
payment.mode_of_payment = "Wire Transfer"
payment.party_type = pe.party_type
payment.party = pe.party
payment.paid_to = self.bank_account if pe.party_type == "Customer" else self.payable_account
payment.paid_from = self.receivable_account if pe.party_type == "Customer" else self.bank_account
payment.paid_amount = payment.received_amount = abs(pe.amount)
payment.reference_no = pe.description
payment.reference_date = pe.transaction_date
payment.save()
for inv_entry in self.payment_invoice_items:
if (pe.description != inv_entry.payment_description or pe.transaction_date != inv_entry.transaction_date): continue
if (pe.party != inv_entry.party): continue
reference = payment.append("references", {})
reference.reference_doctype = inv_entry.invoice_type
reference.reference_name = inv_entry.invoice
reference.allocated_amount = inv_entry.allocated_amount
print ("Adding invoice {0} {1}".format(reference.reference_name, reference.allocated_amount))
payment.setup_party_account_field()
payment.set_missing_values()
#payment.set_exchange_rate()
#payment.set_amounts()
#print("Created payment entry {0}".format(payment.as_dict()))
payment.save()
return payment
def create_journal_entry(self, pe):
je = frappe.new_doc("Journal Entry")
je.is_opening = "No"
je.voucher_type = "Bank Entry"
je.cheque_no = pe.description
je.cheque_date = pe.transaction_date
je.remark = pe.description
je.posting_date = pe.transaction_date
if (pe.amount < 0):
je.append("accounts", {"account": pe.party, "debit_in_account_currency": abs(pe.amount)})
je.append("accounts", {"account": self.bank_account, "credit_in_account_currency": abs(pe.amount)})
else:
je.append("accounts", {"account": pe.party, "credit_in_account_currency": pe.amount})
je.append("accounts", {"account": self.bank_account, "debit_in_account_currency": pe.amount})
je.save()
return je
def update_payment_entry(self, payment):
lst = []
invoices = payment.invoices.strip().split(',')
if (len(invoices) == 0): return
amount = float(abs(payment.amount))
for invoice_entry in invoices:
if (not invoice_entry.strip()): continue
invs = invoice_entry.split('|')
invoice_type, invoice = invs[0], invs[1]
outstanding_amount = frappe.get_value(invoice_type, invoice, 'outstanding_amount')
lst.append(frappe._dict({
'voucher_type': payment.reference_type,
'voucher_no' : payment.reference_name,
'against_voucher_type' : invoice_type,
'against_voucher' : invoice,
'account' : payment.account,
'party_type': payment.party_type,
'party': frappe.get_value("Payment Entry", payment.reference_name, "party"),
'unadjusted_amount' : float(amount),
'allocated_amount' : min(outstanding_amount, amount)
}))
amount -= outstanding_amount
if lst:
from erpnext.accounts.utils import reconcile_against_document
try:
reconcile_against_document(lst)
except:
frappe.throw(_("Exception occurred while reconciling {0}").format(payment.reference_name))
def submit_payment_entries(self):
for payment in self.new_transaction_items:
if payment.reference_name is None: continue
doc = frappe.get_doc(payment.reference_type, payment.reference_name)
if doc.docstatus == 1:
if (payment.reference_type == "Journal Entry"): continue
if doc.unallocated_amount == 0: continue
print("Reconciling payment {0}".format(payment.reference_name))
self.update_payment_entry(payment)
else:
print("Submitting payment {0}".format(payment.reference_name))
if (payment.reference_type == "Payment Entry"):
if (payment.payment_reference):
doc.reference_no = payment.payment_reference
doc.mode_of_payment = payment.mode_of_payment
doc.save()
doc.submit()
self.move_reconciled_entries()
self.populate_matching_invoices()
def move_reconciled_entries(self):
idx = 0
while idx < len(self.new_transaction_items):
entry = self.new_transaction_items[idx]
try:
print("Checking transaction {0}: {2} in {1} entries".format(idx, len(self.new_transaction_items), frappe.safe_decode(entry.description)))
except UnicodeEncodeError:
pass
idx += 1
if entry.reference_name is None: continue
doc = frappe.get_doc(entry.reference_type, entry.reference_name)
if doc.docstatus == 1 and (entry.reference_type == "Journal Entry" or doc.unallocated_amount == 0):
self.remove(entry)
rc_entry = self.append('reconciled_transaction_items', {})
dentry = entry.as_dict()
dentry.pop('idx', None)
rc_entry.update(dentry)
idx -= 1
def get_matching_journal_entries(from_date, to_date, account, against, amount):
query = """select voucher_no, posting_date, account, against, debit_in_account_currency as debit, credit_in_account_currency as credit
from `tabGL Entry`
where posting_date between '{0}' and '{1}' and account = '{2}' and against = '{3}' and debit = '{4}'
""".format(from_date, to_date, account, against, amount)
jv_entries = frappe.db.sql(query, as_dict=True)
#print("voucher query:{0}\n Returned {1} entries".format(query, len(jv_entries)))
return jv_entries
def get_payments_matching_invoice(invoice, amount, pay_date):
query = """select pe.name as reference_name, per.reference_doctype as reference_type, per.outstanding_amount, per.allocated_amount
from `tabPayment Entry Reference` as per JOIN `tabPayment Entry` as pe on pe.name = per.parent
where per.reference_name='{0}' and (posting_date='{1}' or reference_date='{1}') and pe.docstatus != 2
""".format(invoice, pay_date)
payments = frappe.db.sql(query, as_dict=True)
if (len(payments) == 0): return
payment = next((payment for payment in payments if payment.allocated_amount == amount), payments[0])
#Hack: Update the reference type which is set to invoice type
payment.reference_type = "Payment Entry"
return payment
def is_headers_present(headers, row):
for header in headers:
if header not in row:
return False
return True
def get_header_index(headers, row):
header_index = {}
for header in headers:
if header in row:
header_index[header] = row.index(header)
return header_index
def get_transaction_info(headers, header_index, row):
transaction = {}
for header in headers:
transaction[header] = row[header_index[header]]
if (transaction[header] == None):
transaction[header] = ""
return transaction
def get_transaction_entries(file_url, headers):
header_index = {}
rows, transactions = [], []
if (file_url.lower().endswith("xlsx")):
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
rows = read_xlsx_file_from_attached_file(file_url=file_url)
elif (file_url.lower().endswith("csv")):
from frappe.utils.csvutils import read_csv_content
_file = frappe.get_doc("File", {"file_url": file_url})
filepath = _file.get_full_path()
with open(filepath,'rb') as csvfile:
rows = read_csv_content(csvfile.read())
elif (file_url.lower().endswith("xls")):
filename = file_url.split("/")[-1]
rows = get_rows_from_xls_file(filename)
else:
frappe.throw(_("Only .csv and .xlsx files are supported currently"))
stmt_headers = headers.values()
for row in rows:
if len(row) == 0 or row[0] == None or not row[0]: continue
#print("Processing row {0}".format(row))
if header_index:
transaction = get_transaction_info(stmt_headers, header_index, row)
transactions.append(transaction)
elif is_headers_present(stmt_headers, row):
header_index = get_header_index(stmt_headers, row)
return transactions
def get_rows_from_xls_file(filename):
_file = frappe.get_doc("File", {"file_name": filename})
filepath = _file.get_full_path()
import xlrd
book = xlrd.open_workbook(filepath)
sheets = book.sheets()
rows = []
for row in range(1, sheets[0].nrows):
row_values = []
for col in range(1, sheets[0].ncols):
row_values.append(sheets[0].cell_value(row, col))
rows.append(row_values)
return rows

View File

@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Bank Statement Transaction Entry", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Bank Statement Transaction Entry
() => frappe.tests.make('Bank Statement Transaction Entry', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestBankStatementTransactionEntry(unittest.TestCase):
pass

View File

@ -1,365 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-07 13:58:53.827058",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "transaction_date",
"fieldtype": "Date",
"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": "Transaction Date",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 4,
"fieldname": "payment_description",
"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": "Payment Description",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "party_type",
"fieldtype": "Select",
"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": "Party Type",
"length": 0,
"no_copy": 0,
"options": "Customer\nSupplier\nAccount",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "party",
"fieldtype": "Dynamic Link",
"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": "Party",
"length": 0,
"no_copy": 0,
"options": "party_type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "invoice_date",
"fieldtype": "Date",
"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": "Invoice Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoice_type",
"fieldtype": "Select",
"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": "Invoice Type",
"length": 0,
"no_copy": 0,
"options": "Sales Invoice\nPurchase Invoice\nJournal Entry",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "invoice",
"fieldtype": "Dynamic 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": "invoice",
"length": 0,
"no_copy": 0,
"options": "invoice_type",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "outstanding_amount",
"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": "Outstanding Amount",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 1,
"fieldname": "allocated_amount",
"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": "Allocated Amount",
"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,
"translatable": 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-09-14 19:03:30.949831",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Transaction Invoice Item",
"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,
"track_views": 0
}

View File

@ -1,494 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-07 14:03:05.651413",
"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": 1,
"fieldname": "transaction_date",
"fieldtype": "Date",
"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": "Transaction Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": 4,
"fieldname": "description",
"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": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": 1,
"fieldname": "amount",
"fieldtype": "Currency",
"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": "Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "column_break_3",
"fieldtype": "Column 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,
"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": 1,
"fieldname": "party_type",
"fieldtype": "Select",
"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": "Party Type",
"length": 0,
"no_copy": 0,
"options": "Customer\nSupplier\nAccount",
"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": 2,
"fieldname": "party",
"fieldtype": "Dynamic 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": "Party",
"length": 0,
"no_copy": 0,
"options": "party_type",
"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_break_6",
"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,
"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": "reference_type",
"fieldtype": "Select",
"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": "Reference Type",
"length": 0,
"no_copy": 0,
"options": "Payment Entry\nJournal Entry",
"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": "account",
"fieldtype": "Link",
"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": "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": 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": "mode_of_payment",
"fieldtype": "Link",
"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": "Mode of Payment",
"length": 0,
"no_copy": 0,
"options": "Mode of Payment",
"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": "outstanding_amount",
"fieldtype": "Currency",
"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": "outstanding_amount",
"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": "column_break_10",
"fieldtype": "Column 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,
"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": 2,
"fieldname": "reference_name",
"fieldtype": "Dynamic 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": "Reference Name",
"length": 0,
"no_copy": 0,
"options": "reference_type",
"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": "payment_reference",
"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": "Payment Reference",
"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": "invoices",
"fieldtype": "Text",
"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": "Invoices",
"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": 1,
"max_attachments": 0,
"modified": "2017-11-15 19:18:52.876221",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Transaction Payment Item",
"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
}

View File

@ -1,8 +0,0 @@
// Copyright (c) 2017, sathishpy@gmail.com and contributors
// For license information, please see license.txt
frappe.ui.form.on('Bank Statement Settings', {
refresh: function(frm) {
}
});

View File

@ -1,266 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 1,
"beta": 0,
"creation": "2017-11-13 13:38:10.863592",
"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": "bank_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": "Bank Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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,
"default": "'%d/%m/%Y'",
"fieldname": "date_format",
"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": "Date 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
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "statement_header_mapping",
"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": "Statement Header Mapping",
"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": "header_items",
"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": "Statement Headers",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Settings Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "transaction_data_mapping",
"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": "Transaction Data Mapping",
"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": "mapped_items",
"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": "Mapped Items",
"length": 0,
"no_copy": 0,
"options": "Bank Statement Transaction Settings Item",
"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-01-12 10:34:32.840487",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Settings",
"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": "Accounts 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
}

View File

@ -1,11 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class BankStatementSettings(Document):
def autoname(self):
self.name = self.bank_account + "-Mappings"

View File

@ -1,23 +0,0 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Bank Statement Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Bank Statement Settings
() => frappe.tests.make('Bank Statement Settings', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestBankStatementSettings(unittest.TestCase):
pass

View File

@ -1,166 +0,0 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-13 13:42:00.335432",
"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,
"default": "Transaction",
"fieldname": "mapping_type",
"fieldtype": "Select",
"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 Type",
"length": 0,
"no_copy": 0,
"options": "Transaction",
"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": "bank_data",
"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": "Bank Data",
"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,
"default": "Account",
"fieldname": "mapped_data_type",
"fieldtype": "Select",
"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": "Mapped Data Type",
"length": 0,
"no_copy": 0,
"options": "Account\nCustomer\nSupplier\nAccount",
"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": "mapped_data",
"fieldtype": "Dynamic 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": "Mapped Data",
"length": 0,
"no_copy": 0,
"options": "mapped_data_type",
"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-01-08 00:13:49.973501",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Transaction Settings Item",
"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
}

View File

@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, sathishpy@gmail.com and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class BankStatementTransactionSettingsItem(Document):
pass

View File

@ -1,32 +1,70 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Bank Transaction', {
frappe.ui.form.on("Bank Transaction", {
onload(frm) {
frm.set_query('payment_document', 'payment_entries', function() {
frm.set_query("payment_document", "payment_entries", function () {
return {
"filters": {
"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]]
}
filters: {
name: [
"in",
[
"Payment Entry",
"Journal Entry",
"Sales Invoice",
"Purchase Invoice",
"Expense Claim",
],
],
},
};
});
}
},
bank_account: function (frm) {
set_bank_statement_filter(frm);
},
setup: function (frm) {
frm.set_query("party_type", function () {
return {
filters: {
name: ["in", Object.keys(frappe.boot.party_account_types)],
},
};
});
},
});
frappe.ui.form.on('Bank Transaction Payments', {
payment_entries_remove: function(frm, cdt, cdn) {
frappe.ui.form.on("Bank Transaction Payments", {
payment_entries_remove: function (frm, cdt, cdn) {
update_clearance_date(frm, cdt, cdn);
}
},
});
const update_clearance_date = (frm, cdt, cdn) => {
if (frm.doc.docstatus === 1) {
frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment',
{doctype: cdt, docname: cdn})
.then(e => {
frappe
.xcall(
"erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
{ doctype: cdt, docname: cdn }
)
.then((e) => {
if (e == "success") {
frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'});
frappe.show_alert({
message: __("Document {0} successfully uncleared", [e]),
indicator: "green",
});
}
});
}
};
};
function set_bank_statement_filter(frm) {
frm.set_query("bank_statement", function () {
return {
filters: {
bank_account: frm.doc.bank_account,
},
};
});
}

View File

@ -1,833 +1,245 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"actions": [],
"allow_import": 1,
"allow_rename": 0,
"autoname": "naming_series:",
"beta": 0,
"creation": "2018-10-22 18:19:02.784533",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"date",
"column_break_2",
"status",
"bank_account",
"company",
"section_break_4",
"deposit",
"withdrawal",
"column_break_7",
"currency",
"section_break_10",
"description",
"section_break_14",
"reference_number",
"transaction_id",
"payment_entries",
"section_break_18",
"allocated_amount",
"amended_from",
"column_break_17",
"unallocated_amount",
"party_section",
"party_type",
"party"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "ACC-BTN-.YYYY.-",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 1,
"options": "ACC-BTN-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"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": 1,
"translatable": 0,
"unique": 0
"set_only_once": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "date",
"fieldtype": "Date",
"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": "Date",
"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,
"translatable": 0,
"unique": 0
"label": "Date"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_2",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Pending",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "\nPending\nSettled\nUnreconciled\nReconciled",
"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,
"translatable": 0,
"unique": 0
"options": "\nPending\nSettled\nUnreconciled\nReconciled"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "bank_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Bank Account",
"length": 0,
"no_copy": 0,
"options": "Bank 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": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"options": "Bank Account"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_from": "bank_account.company",
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_4",
"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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "debit",
"fieldtype": "Currency",
"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": "Debit",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "credit",
"fieldtype": "Currency",
"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": "Credit",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_7",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "currency",
"fieldtype": "Link",
"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": "Currency",
"length": 0,
"no_copy": 0,
"options": "Currency",
"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,
"translatable": 0,
"unique": 0
"options": "Currency"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_10",
"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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"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": "Description",
"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,
"translatable": 0,
"unique": 0
"label": "Description"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_14",
"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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"allow_on_submit": 1,
"fieldname": "reference_number",
"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": "Reference Number",
"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,
"translatable": 0,
"unique": 0
"label": "Reference Number"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "transaction_id",
"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": "Transaction ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_entries",
"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": "Payment Entries",
"length": 0,
"no_copy": 0,
"options": "Bank Transaction Payments",
"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,
"translatable": 0,
"unique": 0
"options": "Bank Transaction Payments"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_18",
"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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"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": "Allocated Amount",
"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,
"translatable": 0,
"unique": 0
"label": "Allocated Amount"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Bank Transaction",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_17",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "unallocated_amount",
"fieldtype": "Currency",
"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": "Unallocated Amount",
"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,
"translatable": 0,
"unique": 0
"label": "Unallocated Amount"
},
{
"fieldname": "party_section",
"fieldtype": "Section Break",
"label": "Payment From / To"
},
{
"allow_on_submit": 1,
"fieldname": "party_type",
"fieldtype": "Link",
"label": "Party Type",
"options": "DocType"
},
{
"allow_on_submit": 1,
"fieldname": "party",
"fieldtype": "Dynamic Link",
"label": "Party",
"options": "party_type"
},
{
"fieldname": "deposit",
"oldfieldname": "debit",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Deposit"
},
{
"fieldname": "withdrawal",
"oldfieldname": "credit",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Withdrawal"
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-05-11 05:27:55.244721",
"links": [],
"modified": "2020-12-30 19:40:54.221070",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 1,
"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": 1,
"write": 1
},
{
"amend": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 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": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "date",
"sort_order": "DESC",
"title_field": "bank_account",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
"track_changes": 1
}

View File

@ -11,7 +11,7 @@ from frappe import _
class BankTransaction(StatusUpdater):
def after_insert(self):
self.unallocated_amount = abs(flt(self.credit) - flt(self.debit))
self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit))
def on_submit(self):
self.clear_linked_payment_entries()
@ -30,13 +30,13 @@ class BankTransaction(StatusUpdater):
if allocated_amount:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)) - flt(allocated_amount))
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount))
else:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)))
amount = self.debit or self.credit
amount = self.deposit or self.withdrawal
if amount == self.allocated_amount:
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
@ -44,18 +44,11 @@ class BankTransaction(StatusUpdater):
def clear_linked_payment_entries(self):
for payment_entry in self.payment_entries:
allocated_amount = get_total_allocated_amount(payment_entry)
paid_amount = get_paid_amount(payment_entry, self.currency)
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
self.clear_simple_entry(payment_entry)
if paid_amount and allocated_amount:
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).").format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))
else:
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
self.clear_simple_entry(payment_entry)
elif payment_entry.payment_document == "Sales Invoice":
self.clear_sales_invoice(payment_entry)
elif payment_entry.payment_document == "Sales Invoice":
self.clear_sales_invoice(payment_entry)
def clear_simple_entry(self, payment_entry):
frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
@ -112,3 +105,4 @@ def unclear_reference_payment(doctype, docname):
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
return doc.payment_entry

View File

@ -5,10 +5,11 @@ from __future__ import unicode_literals
import frappe
import unittest
import json
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import reconcile_vouchers, get_linked_payments
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
test_dependencies = ["Item", "Cost Center"]
@ -17,7 +18,7 @@ class TestBankTransaction(unittest.TestCase):
def setUp(self):
make_pos_profile()
add_transactions()
add_payments()
add_vouchers()
def tearDown(self):
for bt in frappe.get_all("Bank Transaction"):
@ -38,14 +39,18 @@ class TestBankTransaction(unittest.TestCase):
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
def test_linked_payments(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
linked_payments = get_linked_payments(bank_transaction.name)
self.assertTrue(linked_payments[0].party == "Conrad Electronic")
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
def test_reconcile(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
reconcile(bank_transaction.name, "Payment Entry", payment.name)
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
reconcile_vouchers(bank_transaction.name, vouchers)
unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
self.assertTrue(unallocated_amount == 0)
@ -53,45 +58,40 @@ class TestBankTransaction(unittest.TestCase):
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertTrue(clearance_date is not None)
# Check if ERPNext can correctly fetch a linked payment based on the party
def test_linked_payments_based_on_party(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
linked_payments = get_linked_payments(bank_transaction.name)
self.assertTrue(len(linked_payments)==1)
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
linked_payments = get_linked_payments(bank_transaction.name)
self.assertTrue(linked_payments[0].payment_type == "Pay")
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
print(linked_payments)
self.assertTrue(linked_payments[0][3])
# Check error if already reconciled
def test_already_reconciled(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
reconcile(bank_transaction.name, "Payment Entry", payment.name)
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
reconcile_vouchers(bank_transaction.name, vouchers)
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
# Raise an error if creditor transaction vs creditor payment
def test_invalid_creditor_reconcilation(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690))
self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
# Raise an error if debitor transaction vs debitor payment
def test_invalid_debitor_reconcilation(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080))
self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name)
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
# Raise an error if debitor transaction vs debitor payment
def test_clear_sales_invoice(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
reconcile(bank_transaction.name, "Sales Invoice", payment.name)
vouchers = json.dumps([{
"payment_doctype":"Sales Invoice",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
reconcile_vouchers(bank_transaction.name, vouchers=vouchers)
self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
@ -126,7 +126,7 @@ def add_transactions():
"doctype": "Bank Transaction",
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
"date": "2018-10-23",
"debit": 1200,
"deposit": 1200,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@ -136,7 +136,7 @@ def add_transactions():
"doctype": "Bank Transaction",
"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
"date": "2018-10-23",
"debit": 1700,
"deposit": 1700,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@ -146,7 +146,7 @@ def add_transactions():
"doctype": "Bank Transaction",
"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
"date": "2018-10-26",
"debit": 690,
"withdrawal": 690,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@ -156,7 +156,7 @@ def add_transactions():
"doctype": "Bank Transaction",
"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
"date": "2018-10-27",
"debit": 3900,
"deposit": 3900,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@ -166,7 +166,7 @@ def add_transactions():
"doctype": "Bank Transaction",
"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
"date": "2018-10-27",
"credit": 109080,
"withdrawal": 109080,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
@ -174,7 +174,7 @@ def add_transactions():
frappe.flags.test_bank_transactions_created = True
def add_payments():
def add_vouchers():
if frappe.flags.test_payments_created:
return
@ -192,6 +192,7 @@ def add_payments():
pass
pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24"
@ -242,10 +243,15 @@ def add_payments():
except frappe.DuplicateEntryError:
pass
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900)
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1)
pi.cash_bank_account = "_Test Bank - _TC"
pi.insert()
pi.submit()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "Poore Simon's Oct 18"
pe.reference_date = "2018-10-28"
pe.paid_amount = 690
pe.received_amount = 690
pe.insert()
pe.submit()
@ -295,4 +301,4 @@ def add_payments():
si.save()
si.submit()
frappe.flags.test_payments_created = True
frappe.flags.test_payments_created = True

View File

@ -22,9 +22,10 @@ def validate_company(company):
'allow_account_creation_against_child_company'])
if parent_company and (not allow_account_creation_against_child_company):
frappe.throw(_("""{0} is a child company. Please import accounts against parent company
or enable {1} in company master""").format(frappe.bold(company),
frappe.bold('Allow Account Creation Against Child Company')), title='Wrong Company')
msg = _("{} is a child company. ").format(frappe.bold(company))
msg += _("Please import accounts against parent company or enable {} in company master.").format(
frappe.bold('Allow Account Creation Against Child Company'))
frappe.throw(msg, title=_('Wrong Company'))
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
return False
@ -74,7 +75,9 @@ def generate_data_from_csv(file_doc, as_dict=False):
if as_dict:
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
else:
if not row[1]: row[1] = row[0]
if not row[1]:
row[1] = row[0]
row[3] = row[2]
data.append(row)
# convert csv data
@ -96,7 +99,9 @@ def generate_data_from_excel(file_doc, extension, as_dict=False):
if as_dict:
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
else:
if not row[1]: row[1] = row[0]
if not row[1]:
row[1] = row[0]
row[3] = row[2]
data.append(row)
return data
@ -147,7 +152,13 @@ def build_forest(data):
from frappe import _
for row in data:
account_name, parent_account = row[0:2]
account_name, parent_account, account_number, parent_account_number = row[0:4]
if account_number:
account_name = "{} - {}".format(account_number, account_name)
if parent_account_number:
parent_account_number = cstr(parent_account_number).strip()
parent_account = "{} - {}".format(parent_account_number, parent_account)
if parent_account == account_name == child:
return [parent_account]
elif account_name == child:
@ -159,20 +170,23 @@ def build_forest(data):
charts_map, paths = {}, []
line_no = 3
line_no = 2
error_messages = []
for i in data:
account_name, dummy, account_number, is_group, account_type, root_type = i
account_name, parent_account, account_number, parent_account_number, is_group, account_type, root_type = i
if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
if account_number:
account_number = cstr(account_number).strip()
account_name = "{} - {}".format(account_number, account_name)
charts_map[account_name] = {}
if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
if account_type: charts_map[account_name]["account_type"] = account_type
if root_type: charts_map[account_name]["root_type"] = root_type
if account_number: charts_map[account_name]["account_number"] = account_number
path = return_parent(data, account_name)[::-1]
paths.append(path) # List of path is created
line_no += 1
@ -221,7 +235,7 @@ def download_template(file_type, template_type):
def get_template(template_type):
fields = ["Account Name", "Parent Account", "Account Number", "Is Group", "Account Type", "Root Type"]
fields = ["Account Name", "Parent Account", "Account Number", "Parent Account Number", "Is Group", "Account Type", "Root Type"]
writer = UnicodeWriter()
writer.writerow(fields)
@ -241,23 +255,23 @@ def get_template(template_type):
def get_sample_template(writer):
template = [
["Application Of Funds(Assets)", "", "", 1, "", "Asset"],
["Sources Of Funds(Liabilities)", "", "", 1, "", "Liability"],
["Equity", "", "", 1, "", "Equity"],
["Expenses", "", "", 1, "", "Expense"],
["Income", "", "", 1, "", "Income"],
["Bank Accounts", "Application Of Funds(Assets)", "", 1, "Bank", "Asset"],
["Cash In Hand", "Application Of Funds(Assets)", "", 1, "Cash", "Asset"],
["Stock Assets", "Application Of Funds(Assets)", "", 1, "Stock", "Asset"],
["Cost Of Goods Sold", "Expenses", "", 0, "Cost of Goods Sold", "Expense"],
["Asset Depreciation", "Expenses", "", 0, "Depreciation", "Expense"],
["Fixed Assets", "Application Of Funds(Assets)", "", 0, "Fixed Asset", "Asset"],
["Accounts Payable", "Sources Of Funds(Liabilities)", "", 0, "Payable", "Liability"],
["Accounts Receivable", "Application Of Funds(Assets)", "", 1, "Receivable", "Asset"],
["Stock Expenses", "Expenses", "", 0, "Stock Adjustment", "Expense"],
["Sample Bank", "Bank Accounts", "", 0, "Bank", "Asset"],
["Cash", "Cash In Hand", "", 0, "Cash", "Asset"],
["Stores", "Stock Assets", "", 0, "Stock", "Asset"],
["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"],
["Equity", "", "", "", 1, "", "Equity"],
["Expenses", "", "", "", 1, "", "Expense"],
["Income", "", "", "", 1, "", "Income"],
["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"],
["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"],
["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"],
["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"],
["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"],
["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"],
["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"],
["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"],
["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"],
["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"],
["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"],
["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"],
]
for row in template:

View File

@ -1,7 +1,7 @@
{
"actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:title",
"creation": "2018-11-22 22:45:00.370913",
"doctype": "DocType",
"document_type": "Setup",
@ -20,8 +20,7 @@
"in_list_view": 1,
"label": "Title",
"no_copy": 1,
"reqd": 1,
"unique": 1
"reqd": 1
},
{
"fieldname": "taxes",
@ -33,12 +32,14 @@
{
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1
}
],
"modified": "2020-09-18 17:26:09.703215",
"links": [],
"modified": "2021-03-08 19:50:21.416513",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Item Tax Template",
@ -81,5 +82,6 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "title",
"track_changes": 1
}

View File

@ -11,6 +11,11 @@ class ItemTaxTemplate(Document):
def validate(self):
self.validate_tax_accounts()
def autoname(self):
if self.company and self.title:
abbr = frappe.get_cached_value('Company', self.company, 'abbr')
self.name = '{0} - {1}'.format(self.title, abbr)
def validate_tax_accounts(self):
"""Check whether Tax Rate is not entered twice for same Tax Type"""
check_list = []

View File

@ -102,7 +102,7 @@ class JournalEntry(AccountsController):
if account_currency == previous_account_currency:
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
for account in stock_accounts:
@ -229,11 +229,11 @@ class JournalEntry(AccountsController):
if d.reference_type=="Journal Entry":
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
if account_root_type == "Asset" and flt(d.debit) > 0:
frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
.format(d.account))
frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
.format(d.idx, d.account))
elif account_root_type == "Liability" and flt(d.credit) > 0:
frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
.format(d.account))
frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
.format(d.idx, d.account))
if d.reference_name == self.name:
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
@ -1077,4 +1077,4 @@ def make_reverse_journal_entry(source_name, target_doc=None):
},
}, target_doc)
return doclist
return doclist

View File

@ -12,7 +12,7 @@ class ModeofPayment(Document):
self.validate_accounts()
self.validate_repeating_companies()
self.validate_pos_mode_of_payment()
def validate_repeating_companies(self):
"""Error when Same Company is entered multiple times in accounts"""
accounts_list = []
@ -31,10 +31,10 @@ class ModeofPayment(Document):
def validate_pos_mode_of_payment(self):
if not self.enabled:
pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name))
pos_profiles = list(map(lambda x: x[0], pos_profiles))
if pos_profiles:
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."

View File

@ -121,7 +121,8 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
frappe.render_template('opening_invoice_creation_tool_dashboard', {
data: opening_invoices_summary,
max_count: max_count
})
}),
__("Opening Invoices Summary")
);
section.on('click', '.invoice-link', function() {

View File

@ -64,11 +64,11 @@ class OpeningInvoiceCreationTool(Document):
prepare_invoice_summary(doctype, invoices)
return invoices_summary, max_count
def validate_company(self):
if not self.company:
frappe.throw(_("Please select the Company"))
def set_missing_values(self, row):
row.qty = row.qty or 1.0
row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(self.company)
@ -198,6 +198,7 @@ def start_import(invoices):
try:
publish(idx, len(invoices), d.doctype)
doc = frappe.get_doc(d)
doc.flags.ignore_mandatory = True
doc.insert()
doc.submit()
frappe.db.commit()
@ -210,7 +211,7 @@ def start_import(invoices):
frappe.db.commit()
if errors:
frappe.msgprint(_("You had {} errors while creating opening invoices. Check {} for more details")
.format(errors, "<a href='#List/Error Log' class='variant-click'>Error Log</a>"), indicator="red", title=_("Error Occured"))
.format(errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"), indicator="red", title=_("Error Occured"))
return names
def publish(index, total, doctype):

View File

@ -1,4 +1,3 @@
<h5 style="margin-top: 0px;">{{ __("Opening Invoices Summary") }}</h5>
{% $.each(data, (company, summary) => { %}
<h6 style="margin: 15px 0px -10px 0px;"><a class="company-link"> {{ company }}</a></h6>

View File

@ -536,7 +536,8 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Title",
"print_hide": 1
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "party",
@ -588,7 +589,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-10-30 13:56:20.007336",
"modified": "2021-03-08 13:05:16.958866",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
@ -632,4 +633,4 @@
"sort_order": "DESC",
"title_field": "title",
"track_changes": 1
}
}

View File

@ -244,7 +244,7 @@ class PaymentEntry(AccountsController):
elif self.party_type == "Supplier":
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
elif self.party_type == "Employee":
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
elif self.party_type == "Shareholder":
valid_reference_doctypes = ("Journal Entry")
elif self.party_type == "Donor":
@ -459,6 +459,10 @@ class PaymentEntry(AccountsController):
.format(total_negative_outstanding), InvalidPaymentEntry)
def set_title(self):
if frappe.flags.in_import and self.title:
# do not set title dynamically if title exists during data import.
return
if self.payment_type in ("Receive", "Pay"):
self.title = self.party
else:
@ -608,7 +612,7 @@ class PaymentEntry(AccountsController):
if self.payment_type in ("Receive", "Pay") and self.party:
for d in self.get("references"):
if d.allocated_amount \
and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance"):
and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance", "Gratuity"):
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
def update_expense_claim(self):
@ -946,6 +950,8 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
exchange_rate = ref_doc.get("exchange_rate")
if party_account_currency != ref_doc.currency:
total_amount = flt(total_amount) * flt(exchange_rate)
elif ref_doc.doctype == "Gratuity":
total_amount = ref_doc.amount
if not total_amount:
if party_account_currency == company_currency:
total_amount = ref_doc.base_grand_total
@ -969,6 +975,8 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
if party_account_currency == company_currency:
exchange_rate = 1
elif reference_doctype == "Gratuity":
outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
else:
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
else:
@ -1010,7 +1018,7 @@ def get_amounts_based_on_ref_doc(reference_doctype, ref_doc, party_account_curre
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
elif ref_doc.doctype == "Employee Advance":
total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc)
if not total_amount:
total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
party_account_currency, company_currency, ref_doc)
@ -1174,7 +1182,7 @@ def set_party_type(dt):
party_type = "Customer"
elif dt in ("Purchase Invoice", "Purchase Order"):
party_type = "Supplier"
elif dt in ("Expense Claim", "Employee Advance"):
elif dt in ("Expense Claim", "Employee Advance", "Gratuity"):
party_type = "Employee"
elif dt == "Fees":
party_type = "Student"
@ -1193,6 +1201,8 @@ def set_party_account(dt, dn, doc, party_type):
party_account = doc.advance_account
elif dt == "Expense Claim":
party_account = doc.payable_account
elif dt == "Gratuity":
party_account = doc.payable_account
else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
return party_account
@ -1241,6 +1251,9 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
elif dt == "Donation":
grand_total = doc.amount
outstanding_amount = doc.amount
elif dt == "Gratuity":
grand_total = doc.amount
outstanding_amount = flt(doc.amount) - flt(doc.paid_amount)
else:
if party_account_currency == doc.company_currency:
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
@ -1345,4 +1358,4 @@ def make_payment_order(source_name, target_doc=None):
}, target_doc, set_missing_values)
return doclist
return doclist

View File

@ -88,19 +88,19 @@ class PaymentReconciliation(Document):
voucher_type = ('Sales Invoice'
if self.party_type == 'Customer' else "Purchase Invoice")
return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type,
(sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount,
return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type,
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount,
account_currency as currency
FROM `tab{doc}`, `tabGL Entry`
FROM `tab{doc}` doc, `tabGL Entry` gl
WHERE
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
and `tab{doc}`.{party_type_field} = %(party)s
and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
and `tabGL Entry`.is_cancelled = 0
GROUP BY `tab{doc}`.name
(doc.name = gl.against_voucher or doc.name = gl.voucher_no)
and doc.{party_type_field} = %(party)s
and doc.is_return = 1 and ifnull(doc.return_against, "") = ""
and gl.against_voucher_type = %(voucher_type)s
and doc.docstatus = 1 and gl.party = %(party)s
and gl.party_type = %(party_type)s and gl.account = %(account)s
and gl.is_cancelled = 0
GROUP BY doc.name
Having
amount > 0
""".format(
@ -113,7 +113,7 @@ class PaymentReconciliation(Document):
'party_type': self.party_type,
'voucher_type': voucher_type,
'account': self.receivable_payable_account
}, as_dict=1)
}, as_dict=1, debug=1)
def add_payment_entries(self, entries):
self.set('payments', [])

View File

@ -2,7 +2,7 @@ frappe.listview_settings['Payment Request'] = {
add_fields: ["status"],
get_indicator: function(doc) {
if(doc.status == "Draft") {
return [__("Draft"), "darkgrey", "status,=,Draft"];
return [__("Draft"), "gray", "status,=,Draft"];
}
if(doc.status == "Requested") {
return [__("Requested"), "green", "status,=,Requested"];
@ -19,5 +19,5 @@ frappe.listview_settings['Payment Request'] = {
else if(doc.status == "Cancelled") {
return [__("Cancelled"), "red", "status,=,Cancelled"];
}
}
}
}

View File

@ -6,6 +6,7 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"period_details_section",
"period_start_date",
"period_end_date",
"column_break_3",
@ -65,7 +66,8 @@
},
{
"fieldname": "section_break_5",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "User Details"
},
{
"fieldname": "company",
@ -121,7 +123,7 @@
"collapsible_depends_on": "eval:doc.docstatus==0",
"fieldname": "section_break_13",
"fieldtype": "Section Break",
"label": "Details"
"label": "Totals"
},
{
"default": "0",
@ -196,6 +198,11 @@
"options": "Draft\nSubmitted\nQueued\nCancelled",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "period_details_section",
"fieldtype": "Section Break",
"label": "Period Details"
}
],
"is_submittable": 1,
@ -205,7 +212,7 @@
"link_fieldname": "pos_closing_entry"
}
],
"modified": "2021-01-12 12:21:05.388650",
"modified": "2021-02-01 13:47:20.722104",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Closing Entry",

View File

@ -20,11 +20,16 @@ class POSClosingEntry(StatusUpdater):
self.validate_pos_invoices()
def validate_pos_closing(self):
user = frappe.get_all("POS Closing Entry",
filters = { "user": self.user, "docstatus": 1, "pos_profile": self.pos_profile },
or_filters = {
"period_start_date": ("between", [self.period_start_date, self.period_end_date]),
"period_end_date": ("between", [self.period_start_date, self.period_end_date])
user = frappe.db.sql("""
SELECT name FROM `tabPOS Closing Entry`
WHERE
user = %(user)s AND docstatus = 1 AND pos_profile = %(profile)s AND
(period_start_date between %(start)s and %(end)s OR period_end_date between %(start)s and %(end)s)
""", {
'user': self.user,
'profile': self.pos_profile,
'start': self.period_start_date,
'end': self.period_end_date
})
if user:

View File

@ -13,11 +13,11 @@
"customer",
"customer_name",
"tax_id",
"is_pos",
"pos_profile",
"offline_pos_name",
"is_return",
"consolidated_invoice",
"is_pos",
"is_return",
"update_billed_amount_in_sales_order",
"column_break1",
"company",
"posting_date",
@ -25,10 +25,7 @@
"set_posting_time",
"due_date",
"amended_from",
"returns",
"return_against",
"column_break_21",
"update_billed_amount_in_sales_order",
"accounting_dimensions_section",
"project",
"dimension_col_break",
@ -183,8 +180,7 @@
"column_break_140",
"auto_repeat",
"update_auto_repeat_reference",
"against_income_account",
"pos_total_qty"
"against_income_account"
],
"fields": [
{
@ -265,14 +261,6 @@
"options": "POS Profile",
"print_hide": 1
},
{
"fieldname": "offline_pos_name",
"fieldtype": "Data",
"hidden": 1,
"label": "Offline POS Name",
"print_hide": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"default": "0",
@ -348,26 +336,16 @@
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "return_against",
"fieldname": "returns",
"fieldtype": "Section Break",
"label": "Returns"
},
{
"depends_on": "return_against",
"fieldname": "return_against",
"fieldtype": "Link",
"label": "Return Against POS Invoice",
"label": "Return Against",
"no_copy": 1,
"options": "POS Invoice",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_21",
"fieldtype": "Column Break"
},
{
"default": "0",
"depends_on": "eval: doc.is_return && doc.return_against",
@ -587,19 +565,21 @@
},
{
"fieldname": "sec_warehouse",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Warehouse"
},
{
"depends_on": "update_stock",
"fieldname": "set_warehouse",
"fieldtype": "Link",
"label": "Set Source Warehouse",
"label": "Source Warehouse",
"options": "Warehouse",
"print_hide": 1
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"label": "Items",
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart"
},
@ -1501,7 +1481,7 @@
"allow_on_submit": 1,
"fieldname": "sales_team",
"fieldtype": "Table",
"label": "Sales Team1",
"label": "Sales Team",
"oldfieldname": "sales_team",
"oldfieldtype": "Table",
"options": "Sales Team",
@ -1560,15 +1540,6 @@
"print_hide": 1,
"report_hide": 1
},
{
"fieldname": "pos_total_qty",
"fieldtype": "Float",
"hidden": 1,
"label": "Total Qty",
"print_hide": 1,
"print_hide_if_no_value": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"fieldname": "consolidated_invoice",
@ -1581,7 +1552,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2020-10-30 13:56:51.056083",
"modified": "2021-02-01 15:03:33.800707",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
@ -1626,7 +1597,6 @@
"role": "All"
}
],
"quick_entry": 1,
"search_fields": "posting_date, due_date, customer, base_grand_total, outstanding_amount",
"show_name_in_global_search": 1,
"sort_field": "modified",

View File

@ -178,10 +178,18 @@ class POSInvoice(SalesInvoice):
if d.get("serial_no"):
serial_nos = get_serial_nos(d.serial_no)
for sr in serial_nos:
serial_no_exists = frappe.db.exists("POS Invoice Item", {
"parent": self.return_against,
"serial_no": ["like", d.get("serial_no")]
})
serial_no_exists = frappe.db.sql("""
SELECT name
FROM `tabPOS Invoice Item`
WHERE
parent = %s
and (serial_no = %s
or serial_no like %s
or serial_no like %s
or serial_no like %s
)
""", (self.return_against, sr, sr+'\n%', '%\n'+sr, '%\n'+sr+'\n%'))
if not serial_no_exists:
bold_return_against = frappe.bold(self.return_against)
bold_serial_no = frappe.bold(sr)
@ -189,7 +197,7 @@ class POSInvoice(SalesInvoice):
_("Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {}")
.format(d.idx, bold_serial_no, bold_return_against)
)
def validate_non_stock_items(self):
for d in self.get("items"):
is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
@ -291,7 +299,7 @@ class POSInvoice(SalesInvoice):
if not self.get('payments') and not for_validate:
update_multi_mode_option(self, profile)
if self.is_return and not for_validate:
add_return_modes(self, profile)
@ -428,21 +436,6 @@ class POSInvoice(SalesInvoice):
if pr:
return frappe.get_doc('Payment Request', pr[0][0])
def add_return_modes(doc, pos_profile):
def append_payment(payment_mode):
payment = doc.append('payments', {})
payment.default = payment_mode.default
payment.mode_of_payment = payment_mode.parent
payment.account = payment_mode.default_account
payment.type = payment_mode.type
for pos_payment_method in pos_profile.get('payments'):
pos_payment_method = pos_payment_method.as_dict()
mode_of_payment = pos_payment_method.mode_of_payment
if pos_payment_method.allow_in_returns and not [d for d in doc.get('payments') if d.mode_of_payment == mode_of_payment]:
payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
append_payment(payment_mode[0])
@frappe.whitelist()
def get_stock_availability(item_code, warehouse):
latest_sle = frappe.db.sql("""select qty_after_transaction

View File

@ -99,10 +99,10 @@ class TestPOSInvoice(unittest.TestCase):
item_row = inv.get("items")[0]
add_items = [
(54, '_Test Account Excise Duty @ 12'),
(288, '_Test Account Excise Duty @ 15'),
(144, '_Test Account Excise Duty @ 20'),
(430, '_Test Item Tax Template 1')
(54, '_Test Account Excise Duty @ 12 - _TC'),
(288, '_Test Account Excise Duty @ 15 - _TC'),
(144, '_Test Account Excise Duty @ 20 - _TC'),
(430, '_Test Item Tax Template 1 - _TC')
]
for qty, item_tax_template in add_items:
item_row_copy = copy.deepcopy(item_row)
@ -198,6 +198,65 @@ class TestPOSInvoice(unittest.TestCase):
self.assertEqual(pos_return.get('payments')[0].amount, -500)
self.assertEqual(pos_return.get('payments')[1].amount, -500)
def test_pos_return_for_serialized_item(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item(company='_Test Company',
target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
pos.get("items")[0].serial_no = serial_nos[0]
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
pos.insert()
pos.submit()
pos_return = make_sales_return(pos.name)
pos_return.insert()
pos_return.submit()
self.assertEqual(pos_return.get('items')[0].serial_no, serial_nos[0])
def test_partial_pos_returns(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item(company='_Test Company',
target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
item=se.get("items")[0].item_code, qty=2, rate=1000, do_not_save=1)
pos.get("items")[0].serial_no = serial_nos[0] + "\n" + serial_nos[1]
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 1000, 'default': 1})
pos.insert()
pos.submit()
pos_return1 = make_sales_return(pos.name)
# partial return 1
pos_return1.get('items')[0].qty = -1
pos_return1.get('items')[0].serial_no = serial_nos[0]
pos_return1.insert()
pos_return1.submit()
# partial return 2
pos_return2 = make_sales_return(pos.name)
self.assertEqual(pos_return2.get('items')[0].qty, -1)
self.assertEqual(pos_return2.get('items')[0].serial_no, serial_nos[1])
def test_pos_change_amount(self):
pos = create_pos_invoice(company= "_Test Company", debit_to="Debtors - _TC",
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,

View File

@ -87,6 +87,7 @@
"edit_references",
"sales_order",
"so_detail",
"pos_invoice_item",
"column_break_74",
"delivery_note",
"dn_detail",
@ -790,11 +791,20 @@
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "pos_invoice_item",
"fieldtype": "Data",
"ignore_user_permissions": 1,
"label": "POS Invoice Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-07-22 13:40:34.418346",
"modified": "2021-01-04 17:34:49.924531",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Item",

View File

@ -29,7 +29,7 @@ class POSInvoiceMergeLog(Document):
for d in self.pos_invoices:
status, docstatus, is_return, return_against = frappe.db.get_value(
'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
bold_pos_invoice = frappe.bold(d.pos_invoice)
bold_status = frappe.bold(status)
if docstatus != 1:
@ -58,7 +58,7 @@ class POSInvoiceMergeLog(Document):
sales_invoice, credit_note = "", ""
if sales:
sales_invoice = self.process_merging_into_sales_invoice(sales)
if returns:
credit_note = self.process_merging_into_credit_note(returns)
@ -74,7 +74,7 @@ class POSInvoiceMergeLog(Document):
def process_merging_into_sales_invoice(self, data):
sales_invoice = self.get_new_sales_invoice()
sales_invoice = self.merge_pos_invoice_into(sales_invoice, data)
sales_invoice.is_consolidated = 1
@ -98,19 +98,19 @@ class POSInvoiceMergeLog(Document):
self.consolidated_credit_note = credit_note.name
return credit_note.name
def merge_pos_invoice_into(self, invoice, data):
items, payments, taxes = [], [], []
loyalty_amount_sum, loyalty_points_sum = 0, 0
for doc in data:
map_doc(doc, invoice, table_map={ "doctype": invoice.doctype })
if doc.redeem_loyalty_points:
invoice.loyalty_redemption_account = doc.loyalty_redemption_account
invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center
loyalty_points_sum += doc.loyalty_points
loyalty_amount_sum += doc.loyalty_amount
for item in doc.get('items'):
found = False
for i in items:
@ -118,12 +118,13 @@ class POSInvoiceMergeLog(Document):
i.uom == item.uom and i.net_rate == item.net_rate):
found = True
i.qty = i.qty + item.qty
if not found:
item.rate = item.net_rate
item.price_list_rate = 0
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
items.append(si_item)
for tax in doc.get('taxes'):
found = False
for t in taxes:
@ -162,7 +163,7 @@ class POSInvoiceMergeLog(Document):
invoice.ignore_pricing_rule = 1
return invoice
def get_new_sales_invoice(self):
sales_invoice = frappe.new_doc('Sales Invoice')
sales_invoice.customer = self.customer
@ -194,7 +195,7 @@ def get_all_unconsolidated_invoices():
}
pos_invoices = frappe.db.get_all('POS Invoice', filters=filters,
fields=["name as pos_invoice", 'posting_date', 'grand_total', 'customer'])
return pos_invoices
def get_invoice_customer_map(pos_invoices):
@ -204,7 +205,7 @@ def get_invoice_customer_map(pos_invoices):
customer = invoice.get('customer')
pos_invoice_customer_map.setdefault(customer, [])
pos_invoice_customer_map[customer].append(invoice)
return pos_invoice_customer_map
def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
@ -212,8 +213,8 @@ def consolidate_pos_invoices(pos_invoices=[], closing_entry={}):
invoice_by_customer = get_invoice_customer_map(invoices)
if len(invoices) >= 5 and closing_entry:
enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
closing_entry.set_status(update=True, status='Queued')
enqueue_job(create_merge_logs, invoice_by_customer, closing_entry)
else:
create_merge_logs(invoice_by_customer, closing_entry)
@ -225,8 +226,8 @@ def unconsolidate_pos_invoices(closing_entry):
)
if len(merge_logs) >= 5:
enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
closing_entry.set_status(update=True, status='Queued')
enqueue_job(cancel_merge_logs, merge_logs, closing_entry)
else:
cancel_merge_logs(merge_logs, closing_entry)

View File

@ -6,11 +6,10 @@
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"disabled",
"section_break_2",
"customer",
"company",
"customer",
"country",
"disabled",
"column_break_9",
"warehouse",
"campaign",
@ -61,10 +60,6 @@
"fieldtype": "Check",
"label": "Disabled"
},
{
"fieldname": "section_break_2",
"fieldtype": "Section Break"
},
{
"fieldname": "customer",
"fieldtype": "Link",
@ -348,9 +343,9 @@
"label": "Allow User to Edit Discount"
},
{
"collapsible": 1,
"fieldname": "section_break_23",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Filters"
},
{
"fieldname": "column_break_25",
@ -360,8 +355,29 @@
"icon": "icon-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-01-06 14:42:41.713864",
"links": [
{
"group": "Invoices",
"link_doctype": "Sales Invoice",
"link_fieldname": "pos_profile"
},
{
"group": "Invoices",
"link_doctype": "POS Invoice",
"link_fieldname": "pos_profile"
},
{
"group": "Opening & Closing",
"link_doctype": "POS Opening Entry",
"link_fieldname": "pos_profile"
},
{
"group": "Opening & Closing",
"link_doctype": "POS Closing Entry",
"link_fieldname": "pos_profile"
}
],
"modified": "2021-02-01 13:52:51.081311",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",

View File

@ -136,7 +136,7 @@ class PricingRule(Document):
for d in self.items:
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
if max_discount and flt(self.discount_percentage) > flt(max_discount):
throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount))
throw(_("Max discount allowed for item: {0} is {1}%").format(d.item_code, max_discount))
def validate_price_list_with_currency(self):
if self.currency and self.for_price_list:

View File

@ -1378,7 +1378,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2021-03-09 21:56:28.748582",
"modified": "2021-03-09 21:15:30.422084",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@ -968,7 +968,7 @@ class PurchaseInvoice(BuyingController):
# base_rounding_adjustment may become zero due to small precision
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
if self.rounding_adjustment and self.base_rounding_adjustment:
if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)

View File

@ -19,7 +19,7 @@ frappe.listview_settings['Purchase Invoice'] = {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
}
} else if (cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
return [__("Return"), "gray", "is_return,=,Yes"];
} else if (doc.company == doc.represents_company && doc.is_internal_supplier) {
return [__("Internal Transfer"), "darkgrey", "outstanding_amount,=,0"];
} else if (flt(doc.outstanding_amount)==0 && doc.docstatus==1) {

View File

@ -18,7 +18,7 @@
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"item_code": "_Test Item Home Desktop 100",
"item_name": "_Test Item Home Desktop 100",
"item_tax_template": "_Test Account Excise Duty @ 10",
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
"parentfield": "items",
"qty": 10,
"rate": 50,

View File

@ -6,8 +6,5 @@ import frappe
from frappe.model.document import Document
from erpnext.controllers.print_settings import print_settings_for_item_table
class PurchaseInvoiceItem(Document):
def __setup__(self):
print_settings_for_item_table(self)
pass

View File

@ -669,12 +669,12 @@ frappe.ui.form.on('Sales Invoice', {
};
},
// When multiple companies are set up. in case company name is changed set default company address
company:function(frm){
if (frm.doc.company)
{
company: function(frm){
if (frm.doc.company) {
frappe.call({
method:"erpnext.setup.doctype.company.company.get_default_company_address",
args:{name:frm.doc.company, existing_address: frm.doc.company_address},
method: "erpnext.setup.doctype.company.company.get_default_company_address",
args: {name:frm.doc.company, existing_address: frm.doc.company_address || ""},
debounce: 2000,
callback: function(r){
if (r.message){
frm.set_value("company_address",r.message)
@ -695,6 +695,7 @@ frappe.ui.form.on('Sales Invoice', {
refresh_field(['timesheets'])
}
})
frm.refresh();
},
onload: function(frm) {
@ -810,6 +811,65 @@ frappe.ui.form.on('Sales Invoice', {
},
refresh: function(frm) {
if (frm.doc.project) {
frm.add_custom_button(__('Fetch Timesheet'), function() {
let d = new frappe.ui.Dialog({
title: __('Fetch Timesheet'),
fields: [
{
"label" : "From",
"fieldname": "from_time",
"fieldtype": "Date",
"reqd": 1,
},
{
fieldtype: 'Column Break',
fieldname: 'col_break_1',
},
{
"label" : "To",
"fieldname": "to_time",
"fieldtype": "Date",
"reqd": 1,
}
],
primary_action: function() {
let data = d.get_values();
frappe.call({
method: "erpnext.projects.doctype.timesheet.timesheet.get_projectwise_timesheet_data",
args: {
from_time: data.from_time,
to_time: data.to_time,
project: frm.doc.project
},
callback: function(r) {
if(!r.exc) {
if(r.message.length > 0) {
frm.clear_table('timesheets')
r.message.forEach((d) => {
frm.add_child('timesheets',{
'time_sheet': d.parent,
'billing_hours': d.billing_hours,
'billing_amount': d.billing_amt,
'timesheet_detail': d.name
});
});
frm.refresh_field('timesheets')
}
else {
frappe.msgprint(__('No Timesheet Found.'))
}
d.hide();
}
}
});
},
primary_action_label: __('Get Timesheets')
});
d.show();
})
}
if (frappe.boot.active_domains.includes("Healthcare")) {
frm.set_df_property("patient", "hidden", 0);
frm.set_df_property("patient_name", "hidden", 0);

View File

@ -12,11 +12,11 @@
"customer",
"customer_name",
"tax_id",
"pos_profile",
"is_pos",
"is_consolidated",
"pos_profile",
"offline_pos_name",
"is_return",
"update_billed_amount_in_sales_order",
"column_break1",
"company",
"company_tax_id",
@ -24,11 +24,8 @@
"posting_time",
"set_posting_time",
"due_date",
"amended_from",
"returns",
"return_against",
"column_break_21",
"update_billed_amount_in_sales_order",
"amended_from",
"accounting_dimensions_section",
"project",
"dimension_col_break",
@ -188,8 +185,7 @@
"column_break_140",
"auto_repeat",
"update_auto_repeat_reference",
"against_income_account",
"pos_total_qty"
"against_income_account"
],
"fields": [
{
@ -296,16 +292,6 @@
"options": "POS Profile",
"print_hide": 1
},
{
"fieldname": "offline_pos_name",
"fieldtype": "Data",
"hidden": 1,
"hide_days": 1,
"hide_seconds": 1,
"label": "Offline POS Name",
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fieldname": "is_return",
@ -404,33 +390,19 @@
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "return_against",
"fieldname": "returns",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Returns"
},
{
"depends_on": "return_against",
"fieldname": "return_against",
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"label": "Return Against Sales Invoice",
"label": "Return Against",
"no_copy": 1,
"options": "Sales Invoice",
"print_hide": 1,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "column_break_21",
"fieldtype": "Column Break",
"hide_days": 1,
"hide_seconds": 1
},
{
"default": "0",
"depends_on": "eval: doc.is_return && doc.return_against",
@ -678,7 +650,8 @@
"fieldname": "sec_warehouse",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1
"hide_seconds": 1,
"label": "Warehouse"
},
{
"depends_on": "update_stock",
@ -686,7 +659,7 @@
"fieldtype": "Link",
"hide_days": 1,
"hide_seconds": 1,
"label": "Set Source Warehouse",
"label": "Source Warehouse",
"options": "Warehouse",
"print_hide": 1
},
@ -695,6 +668,7 @@
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Items",
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart"
},
@ -1904,17 +1878,6 @@
"print_hide": 1,
"report_hide": 1
},
{
"fieldname": "pos_total_qty",
"fieldtype": "Float",
"hidden": 1,
"hide_days": 1,
"hide_seconds": 1,
"label": "Total Qty",
"print_hide": 1,
"print_hide_if_no_value": 1,
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
@ -1995,7 +1958,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2021-01-12 12:16:15.192520",
"modified": "2021-02-01 15:42:26.261540",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@ -21,6 +21,7 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
from erpnext.accounts.deferred_revenue import validate_service_stop_date
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
from frappe.model.utils import get_fetch_values
from frappe.contacts.doctype.address.address import get_address_display
@ -55,7 +56,7 @@ class SalesInvoice(SellingController):
"""Set indicator for portal"""
if self.outstanding_amount < 0:
self.indicator_title = _("Credit Note Issued")
self.indicator_color = "darkgrey"
self.indicator_color = "gray"
elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()):
self.indicator_color = "orange"
self.indicator_title = _("Unpaid")
@ -64,7 +65,7 @@ class SalesInvoice(SellingController):
self.indicator_title = _("Overdue")
elif cint(self.is_return) == 1:
self.indicator_title = _("Return")
self.indicator_color = "darkgrey"
self.indicator_color = "gray"
else:
self.indicator_color = "green"
self.indicator_title = _("Paid")
@ -75,6 +76,8 @@ class SalesInvoice(SellingController):
if not self.is_pos:
self.so_dn_required()
self.set_tax_withholding()
self.validate_proj_cust()
self.validate_pos_return()
@ -153,6 +156,32 @@ class SalesInvoice(SellingController):
if cost_center_company != self.company:
frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
def set_tax_withholding(self):
tax_withholding_details = get_party_tax_withholding_details(self)
if not tax_withholding_details:
return
accounts = []
tax_withholding_account = tax_withholding_details.get("account_head")
for d in self.taxes:
if d.account_head == tax_withholding_account:
d.update(tax_withholding_details)
accounts.append(d.account_head)
if not accounts or tax_withholding_account not in accounts:
self.append("taxes", tax_withholding_details)
to_remove = [d for d in self.taxes
if not d.tax_amount and d.charge_type == "Actual" and d.account_head == tax_withholding_account]
for d in to_remove:
self.remove(d)
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()
def before_save(self):
set_account_for_mode_of_payment(self)
@ -1033,7 +1062,8 @@ class SalesInvoice(SellingController):
)
def make_gle_for_rounding_adjustment(self, gl_entries):
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment \
and not self.is_internal_transfer():
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)

View File

@ -10,8 +10,8 @@ frappe.listview_settings['Sales Invoice'] = {
"Draft": "grey",
"Unpaid": "orange",
"Paid": "green",
"Return": "darkgrey",
"Credit Note Issued": "darkgrey",
"Return": "gray",
"Credit Note Issued": "gray",
"Unpaid and Discounted": "orange",
"Overdue and Discounted": "red",
"Overdue": "red",

View File

@ -148,7 +148,7 @@
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"item_code": "_Test Item Home Desktop 100",
"item_name": "_Test Item Home Desktop 100",
"item_tax_template": "_Test Account Excise Duty @ 10",
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
"parentfield": "items",
"price_list_rate": 50,
"qty": 10,
@ -276,7 +276,7 @@
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"item_code": "_Test Item Home Desktop 100",
"item_name": "_Test Item Home Desktop 100",
"item_tax_template": "_Test Account Excise Duty @ 10",
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
"parentfield": "items",
"price_list_rate": 62.5,
"qty": 10,

View File

@ -405,10 +405,10 @@ class TestSalesInvoice(unittest.TestCase):
item_row = si.get("items")[0]
add_items = [
(54, '_Test Account Excise Duty @ 12'),
(288, '_Test Account Excise Duty @ 15'),
(144, '_Test Account Excise Duty @ 20'),
(430, '_Test Item Tax Template 1')
(54, '_Test Account Excise Duty @ 12 - _TC'),
(288, '_Test Account Excise Duty @ 15 - _TC'),
(144, '_Test Account Excise Duty @ 20 - _TC'),
(430, '_Test Item Tax Template 1 - _TC')
]
for qty, item_tax_template in add_items:
item_row_copy = copy.deepcopy(item_row)
@ -2077,14 +2077,14 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
item.save()
item.append("taxes", {
"item_tax_template": "_Test Item Tax Template 1",
"item_tax_template": "_Test Item Tax Template 1 - _TC",
"valid_from": add_days(nowdate(), 1)
})
item.save()
sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1"
sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1 - _TC"
self.assertRaises(frappe.ValidationError, sales_invoice.save)
item.taxes = []

View File

@ -5,8 +5,6 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from erpnext.controllers.print_settings import print_settings_for_item_table
class SalesInvoiceItem(Document):
def __setup__(self):
print_settings_for_item_table(self)
pass

View File

@ -5,3 +5,25 @@ cur_frm.cscript.tax_table = "Sales Taxes and Charges";
{% include "erpnext/public/js/controllers/accounts.js" %}
frappe.tour['Sales Taxes and Charges Template'] = [
{
fieldname: "title",
title: __("Title"),
description: __("A name by which you will identify this template. You can change this later."),
},
{
fieldname: "company",
title: __("Company"),
description: __("Company for which this tax template will be applicable"),
},
{
fieldname: "is_default",
title: __("Is this Default?"),
description: __("Set this template as the default for all sales transactions"),
},
{
fieldname: "taxes",
title: __("Taxes Table"),
description: __("You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount."),
}
];

View File

@ -10,6 +10,14 @@ frappe.ui.form.on('Subscription', {
}
}
});
frm.set_query('cost_center', function() {
return {
filters: {
company: frm.doc.company
}
};
});
},
refresh: function(frm) {

View File

@ -7,9 +7,10 @@
"engine": "InnoDB",
"field_order": [
"party_type",
"status",
"cb_1",
"party",
"cb_1",
"company",
"status",
"subscription_period",
"start_date",
"end_date",
@ -44,80 +45,107 @@
{
"allow_on_submit": 1,
"fieldname": "cb_1",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
"options": "\nTrialling\nActive\nPast Due Date\nCancelled\nUnpaid\nCompleted",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "subscription_period",
"fieldtype": "Section Break",
"label": "Subscription Period"
"label": "Subscription Period",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "cancelation_date",
"fieldtype": "Date",
"label": "Cancelation Date",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "trial_period_start",
"fieldtype": "Date",
"label": "Trial Period Start Date",
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.trial_period_start",
"fieldname": "trial_period_end",
"fieldtype": "Date",
"label": "Trial Period End Date",
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_11",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "current_invoice_start",
"fieldtype": "Date",
"label": "Current Invoice Start Date",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "current_invoice_end",
"fieldtype": "Date",
"label": "Current Invoice End Date",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"description": "Number of days that the subscriber has to pay invoices generated by this subscription",
"fieldname": "days_until_due",
"fieldtype": "Int",
"label": "Days Until Due"
"label": "Days Until Due",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "cancel_at_period_end",
"fieldtype": "Check",
"label": "Cancel At End Of Period"
"label": "Cancel At End Of Period",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "generate_invoice_at_period_start",
"fieldtype": "Check",
"label": "Generate Invoice At Beginning Of Period"
"label": "Generate Invoice At Beginning Of Period",
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "sb_4",
"fieldtype": "Section Break",
"label": "Plans"
"label": "Plans",
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
@ -125,62 +153,84 @@
"fieldtype": "Table",
"label": "Plans",
"options": "Subscription Plan Detail",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:['Customer', 'Supplier'].includes(doc.party_type)",
"fieldname": "sb_1",
"fieldtype": "Section Break",
"label": "Taxes"
"label": "Taxes",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "sb_2",
"fieldtype": "Section Break",
"label": "Discounts"
"label": "Discounts",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "apply_additional_discount",
"fieldtype": "Select",
"label": "Apply Additional Discount On",
"options": "\nGrand Total\nNet Total"
"options": "\nGrand Total\nNet Total",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "cb_2",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "additional_discount_percentage",
"fieldtype": "Percent",
"label": "Additional DIscount Percentage"
"label": "Additional DIscount Percentage",
"show_days": 1,
"show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "additional_discount_amount",
"fieldtype": "Currency",
"label": "Additional DIscount Amount"
"label": "Additional DIscount Amount",
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.invoices",
"fieldname": "sb_3",
"fieldtype": "Section Break",
"label": "Invoices"
"label": "Invoices",
"show_days": 1,
"show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "invoices",
"fieldtype": "Table",
"label": "Invoices",
"options": "Subscription Invoice"
"options": "Subscription Invoice",
"show_days": 1,
"show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
"label": "Accounting Dimensions",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "party_type",
@ -188,7 +238,9 @@
"label": "Party Type",
"options": "DocType",
"reqd": 1,
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "party",
@ -197,21 +249,27 @@
"label": "Party",
"options": "party_type",
"reqd": 1,
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.party_type === 'Customer'",
"fieldname": "sales_tax_template",
"fieldtype": "Link",
"label": "Sales Taxes and Charges Template",
"options": "Sales Taxes and Charges Template"
"options": "Sales Taxes and Charges Template",
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.party_type === 'Supplier'",
"fieldname": "purchase_tax_template",
"fieldtype": "Link",
"label": "Purchase Taxes and Charges Template",
"options": "Purchase Taxes and Charges Template"
"options": "Purchase Taxes and Charges Template",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
@ -219,36 +277,55 @@
"fieldname": "follow_calendar_months",
"fieldtype": "Check",
"label": "Follow Calendar Months",
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"description": "New invoices will be generated as per schedule even if current invoices are unpaid or past due date",
"fieldname": "generate_new_invoices_past_due_date",
"fieldtype": "Check",
"label": "Generate New Invoices Past Due Date"
"label": "Generate New Invoices Past Due Date",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "end_date",
"fieldtype": "Date",
"label": "Subscription End Date",
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "start_date",
"fieldtype": "Date",
"label": "Subscription Start Date",
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
"options": "Cost Center",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"show_days": 1,
"show_seconds": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-06-25 10:52:52.265105",
"modified": "2021-02-09 15:44:20.024789",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription",

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
@ -5,12 +6,13 @@
from __future__ import unicode_literals
import frappe
import erpnext
from frappe import _
from frappe.model.document import Document
from frappe.utils.data import nowdate, getdate, cstr, cint, add_days, date_diff, get_last_day, add_to_date, flt
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from erpnext import get_default_company
class Subscription(Document):
def before_insert(self):
@ -243,6 +245,7 @@ class Subscription(Document):
self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval())
self.validate_end_date()
self.validate_to_follow_calendar_months()
self.cost_center = erpnext.get_default_cost_center(self.get('company'))
def validate_trial_period(self):
"""
@ -304,6 +307,14 @@ class Subscription(Document):
doctype = 'Sales Invoice' if self.party_type == 'Customer' else 'Purchase Invoice'
invoice = frappe.new_doc(doctype)
# For backward compatibility
# Earlier subscription didn't had any company field
company = self.get('company') or get_default_company()
if not company:
frappe.throw(_("Company is mandatory was generating invoice. Please set default company in Global Defaults"))
invoice.company = company
invoice.set_posting_time = 1
invoice.posting_date = self.current_invoice_start if self.generate_invoice_at_period_start \
else self.current_invoice_end
@ -330,6 +341,7 @@ class Subscription(Document):
# for that reason
items_list = self.get_items_from_plans(self.plans, prorate)
for item in items_list:
item['cost_center'] = self.cost_center
invoice.append('items', item)
# Taxes
@ -380,7 +392,8 @@ class Subscription(Document):
Returns the `Item`s linked to `Subscription Plan`
"""
if prorate:
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start)
prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start,
self.generate_invoice_at_period_start)
items = []
party = self.party
@ -583,10 +596,13 @@ def get_calendar_months(billing_interval):
return calendar_months
def get_prorata_factor(period_end, period_start):
diff = flt(date_diff(nowdate(), period_start) + 1)
plan_days = flt(date_diff(period_end, period_start) + 1)
prorate_factor = diff / plan_days
def get_prorata_factor(period_end, period_start, is_prepaid):
if is_prepaid:
prorate_factor = 1
else:
diff = flt(date_diff(nowdate(), period_start) + 1)
plan_days = flt(date_diff(period_end, period_start) + 1)
prorate_factor = diff / plan_days
return prorate_factor

View File

@ -11,7 +11,7 @@ frappe.listview_settings['Subscription'] = {
} else if(doc.status === 'Unpaid') {
return [__("Unpaid"), "red"];
} else if(doc.status === 'Cancelled') {
return [__("Cancelled"), "darkgrey"];
return [__("Cancelled"), "gray"];
}
}
};

View File

@ -321,7 +321,8 @@ class TestSubscription(unittest.TestCase):
self.assertEqual(
flt(
get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start),
get_prorata_factor(subscription.current_invoice_end, subscription.current_invoice_start,
subscription.generate_invoice_at_period_start),
2),
flt(prorate_factor, 2)
)
@ -561,9 +562,7 @@ class TestSubscription(unittest.TestCase):
current_inv = subscription.get_current_invoice()
self.assertEqual(current_inv.status, "Unpaid")
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
prorate_factor = flt(diff / plan_days)
prorate_factor = 1
self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2))

View File

@ -13,21 +13,28 @@
"fieldname": "document_type",
"fieldtype": "Link",
"label": "Document Type ",
"no_copy": 1,
"options": "DocType",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "invoice",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Invoice",
"no_copy": 1,
"options": "document_type",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-06-01 22:23:54.462718",
"modified": "2021-02-09 15:43:32.026233",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Invoice",

View File

@ -11,15 +11,18 @@
],
"fields": [
{
"allow_in_quick_entry": 1,
"fieldname": "title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Title",
"reqd": 1,
"unique": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-08-30 19:41:25.783852",
"modified": "2021-03-03 11:50:38.748872",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Category",

View File

@ -12,37 +12,62 @@ from erpnext.accounts.utils import get_fiscal_year
class TaxWithholdingCategory(Document):
pass
def get_party_tax_withholding_details(ref_doc, tax_withholding_category=None):
def get_party_details(inv):
party_type, party = '', ''
if inv.doctype == 'Sales Invoice':
party_type = 'Customer'
party = inv.customer
else:
party_type = 'Supplier'
party = inv.supplier
return party_type, party
def get_party_tax_withholding_details(inv, tax_withholding_category=None):
pan_no = ''
suppliers = []
parties = []
party_type, party = get_party_details(inv)
if not tax_withholding_category:
tax_withholding_category, pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, ['tax_withholding_category', 'pan'])
tax_withholding_category, pan_no = frappe.db.get_value(party_type, party, ['tax_withholding_category', 'pan'])
if not tax_withholding_category:
return
# if tax_withholding_category passed as an argument but not pan_no
if not pan_no:
pan_no = frappe.db.get_value('Supplier', ref_doc.supplier, 'pan')
pan_no = frappe.db.get_value(party_type, party, 'pan')
# Get others suppliers with the same PAN No
if pan_no:
suppliers = [d.name for d in frappe.get_all('Supplier', fields=['name'], filters={'pan': pan_no})]
parties = frappe.get_all(party_type, filters={ 'pan': pan_no }, pluck='name')
if not suppliers:
suppliers.append(ref_doc.supplier)
if not parties:
parties.append(party)
fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company)
tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
fy = get_fiscal_year(ref_doc.posting_date, company=ref_doc.company)
tax_details = get_tax_withholding_details(tax_withholding_category, fy[0], ref_doc.company)
if not tax_details:
frappe.throw(_('Please set associated account in Tax Withholding Category {0} against Company {1}')
.format(tax_withholding_category, ref_doc.company))
.format(tax_withholding_category, inv.company))
tds_amount = get_tds_amount(suppliers, ref_doc.net_total, ref_doc.company,
tax_details, fy, ref_doc.posting_date, pan_no)
if party_type == 'Customer' and not tax_details.cumulative_threshold:
# TCS is only chargeable on sum of invoiced value
frappe.throw(_('Tax Withholding Category {} against Company {} for Customer {} should have Cumulative Threshold value.')
.format(tax_withholding_category, inv.company, party))
tax_row = get_tax_row(tax_details, tds_amount)
tax_amount, tax_deducted = get_tax_amount(
party_type, parties,
inv, tax_details,
fiscal_year, pan_no
)
if party_type == 'Supplier':
tax_row = get_tax_row_for_tds(tax_details, tax_amount)
else:
tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
return tax_row
@ -69,147 +94,254 @@ def get_tax_withholding_rates(tax_withholding, fiscal_year):
frappe.throw(_("No Tax Withholding data found for the current Fiscal Year."))
def get_tax_row(tax_details, tds_amount):
return {
def get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted):
row = {
"category": "Total",
"add_deduct_tax": "Deduct",
"charge_type": "Actual",
"account_head": tax_details.account_head,
"tax_amount": tax_amount,
"description": tax_details.description,
"tax_amount": tds_amount
"account_head": tax_details.account_head
}
def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_details, posting_date, pan_no=None):
fiscal_year, year_start_date, year_end_date = fiscal_year_details
tds_amount = 0
tds_deducted = 0
if tax_deducted:
# TCS already deducted on previous invoices
# So, TCS will be calculated by 'Previous Row Total'
def _get_tds(amount, rate):
if amount <= 0:
return 0
return amount * rate / 100
ldc_name = frappe.db.get_value('Lower Deduction Certificate',
{
'pan_no': pan_no,
'fiscal_year': fiscal_year
}, 'name')
ldc = ''
if ldc_name:
ldc = frappe.get_doc('Lower Deduction Certificate', ldc_name)
entries = frappe.db.sql("""
select voucher_no, credit
from `tabGL Entry`
where company = %s and
party in %s and fiscal_year=%s and credit > 0
and is_opening = 'No'
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
vouchers = [d.voucher_no for d in entries]
advance_vouchers = get_advance_vouchers(suppliers, fiscal_year=fiscal_year, company=company)
tds_vouchers = vouchers + advance_vouchers
if tds_vouchers:
tds_deducted = frappe.db.sql("""
SELECT sum(credit) FROM `tabGL Entry`
WHERE
account=%s and fiscal_year=%s and credit > 0
and voucher_no in ({0})""". format(','.join(['%s'] * len(tds_vouchers))),
((tax_details.account_head, fiscal_year) + tuple(tds_vouchers)))
tds_deducted = tds_deducted[0][0] if tds_deducted and tds_deducted[0][0] else 0
if tds_deducted:
if ldc:
limit_consumed = frappe.db.get_value('Purchase Invoice',
{
'supplier': ('in', suppliers),
'apply_tds': 1,
'docstatus': 1
}, 'sum(net_total)')
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total,
ldc.certificate_limit):
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
taxes_excluding_tcs = [d for d in inv.taxes if d.account_head != tax_details.account_head]
if taxes_excluding_tcs:
# chargeable amount is the total amount after other charges are applied
row.update({
"charge_type": "On Previous Row Total",
"row_id": len(taxes_excluding_tcs),
"rate": tax_details.rate
})
else:
tds_amount = _get_tds(net_total, tax_details.rate)
else:
supplier_credit_amount = frappe.get_all('Purchase Invoice',
fields = ['sum(net_total)'],
filters = {'name': ('in', vouchers), 'docstatus': 1, "apply_tds": 1}, as_list=1)
# if only TCS is to be charged, then net total is chargeable amount
row.update({
"charge_type": "On Net Total",
"rate": tax_details.rate
})
supplier_credit_amount = (supplier_credit_amount[0][0]
if supplier_credit_amount and supplier_credit_amount[0][0] else 0)
return row
jv_supplier_credit_amt = frappe.get_all('Journal Entry Account',
fields = ['sum(credit_in_account_currency)'],
filters = {
'parent': ('in', vouchers), 'docstatus': 1,
'party': ('in', suppliers),
'reference_type': ('not in', ['Purchase Invoice'])
}, as_list=1)
def get_tax_row_for_tds(tax_details, tax_amount):
return {
"category": "Total",
"charge_type": "Actual",
"tax_amount": tax_amount,
"add_deduct_tax": "Deduct",
"description": tax_details.description,
"account_head": tax_details.account_head
}
supplier_credit_amount += (jv_supplier_credit_amt[0][0]
if jv_supplier_credit_amt and jv_supplier_credit_amt[0][0] else 0)
def get_lower_deduction_certificate(fiscal_year, pan_no):
ldc_name = frappe.db.get_value('Lower Deduction Certificate', { 'pan_no': pan_no, 'fiscal_year': fiscal_year }, 'name')
if ldc_name:
return frappe.get_doc('Lower Deduction Certificate', ldc_name)
supplier_credit_amount += net_total
def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None):
fiscal_year = fiscal_year_details[0]
debit_note_amount = get_debit_note_amount(suppliers, year_start_date, year_end_date)
supplier_credit_amount -= debit_note_amount
vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type)
taxable_vouchers = vouchers + advance_vouchers
if ((tax_details.get('threshold', 0) and supplier_credit_amount >= tax_details.threshold)
or (tax_details.get('cumulative_threshold', 0) and supplier_credit_amount >= tax_details.cumulative_threshold)):
tax_deducted = 0
if taxable_vouchers:
tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
if ldc and is_valid_certificate(ldc.valid_from, ldc.valid_upto, posting_date, tds_deducted, net_total,
ldc.certificate_limit):
tds_amount = get_ltds_amount(supplier_credit_amount, 0, ldc.certificate_limit, ldc.rate,
tax_details)
tax_amount = 0
posting_date = inv.posting_date
if party_type == 'Supplier':
ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
if tax_deducted:
net_total = inv.net_total
if ldc:
tax_amount = get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total)
else:
tds_amount = _get_tds(supplier_credit_amount, tax_details.rate)
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
else:
tax_amount = get_tds_amount(
ldc, parties, inv, tax_details,
fiscal_year_details, tax_deducted, vouchers
)
elif party_type == 'Customer':
if tax_deducted:
# if already TCS is charged, then amount will be calculated based on 'Previous Row Total'
tax_amount = 0
else:
# if no TCS has been charged in FY,
# then chargeable value is "prev invoices + advances" value which cross the threshold
tax_amount = get_tcs_amount(
parties, inv, tax_details,
fiscal_year_details, vouchers, advance_vouchers
)
return tax_amount, tax_deducted
def get_invoice_vouchers(parties, fiscal_year, company, party_type='Supplier'):
dr_or_cr = 'credit' if party_type == 'Supplier' else 'debit'
filters = {
dr_or_cr: ['>', 0],
'company': company,
'party_type': party_type,
'party': ['in', parties],
'fiscal_year': fiscal_year,
'is_opening': 'No',
'is_cancelled': 0
}
return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck="voucher_no") or [""]
def get_advance_vouchers(parties, fiscal_year=None, company=None, from_date=None, to_date=None, party_type='Supplier'):
# for advance vouchers, debit and credit is reversed
dr_or_cr = 'debit' if party_type == 'Supplier' else 'credit'
filters = {
dr_or_cr: ['>', 0],
'is_opening': 'No',
'is_cancelled': 0,
'party_type': party_type,
'party': ['in', parties],
'against_voucher': ['is', 'not set']
}
if fiscal_year:
filters['fiscal_year'] = fiscal_year
if company:
filters['company'] = company
if from_date and to_date:
filters['posting_date'] = ['between', (from_date, to_date)]
return frappe.get_all('GL Entry', filters=filters, distinct=1, pluck='voucher_no') or [""]
def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details):
# check if TDS / TCS account is already charged on taxable vouchers
filters = {
'is_cancelled': 0,
'credit': ['>', 0],
'fiscal_year': fiscal_year,
'account': tax_details.account_head,
'voucher_no': ['in', taxable_vouchers],
}
field = "sum(credit)"
return frappe.db.get_value('GL Entry', filters, field) or 0.0
def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers):
tds_amount = 0
supp_credit_amt = frappe.db.get_value('Purchase Invoice', {
'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1
}, 'sum(net_total)') or 0.0
supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', {
'parent': ('in', vouchers), 'docstatus': 1,
'party': ('in', parties), 'reference_type': ('!=', 'Purchase Invoice')
}, 'sum(credit_in_account_currency)') or 0.0
supp_credit_amt += supp_jv_credit_amt
supp_credit_amt += inv.net_total
debit_note_amount = get_debit_note_amount(parties, fiscal_year_details, inv.company)
supp_credit_amt -= debit_note_amount
threshold = tax_details.get('threshold', 0)
cumulative_threshold = tax_details.get('cumulative_threshold', 0)
if ((threshold and supp_credit_amt >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
if ldc and is_valid_certificate(
ldc.valid_from, ldc.valid_upto,
inv.posting_date, tax_deducted,
inv.net_total, ldc.certificate_limit
):
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
else:
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
return tds_amount
def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=None, to_date=None):
condition = "fiscal_year=%s" % fiscal_year
def get_tcs_amount(parties, inv, tax_details, fiscal_year_details, vouchers, adv_vouchers):
tcs_amount = 0
fiscal_year, _, _ = fiscal_year_details
# sum of debit entries made from sales invoices
invoiced_amt = frappe.db.get_value('GL Entry', {
'is_cancelled': 0,
'party': ['in', parties],
'company': inv.company,
'voucher_no': ['in', vouchers],
}, 'sum(debit)') or 0.0
# sum of credit entries made from PE / JV with unset 'against voucher'
advance_amt = frappe.db.get_value('GL Entry', {
'is_cancelled': 0,
'party': ['in', parties],
'company': inv.company,
'voucher_no': ['in', adv_vouchers],
}, 'sum(credit)') or 0.0
# sum of credit entries made from sales invoice
credit_note_amt = frappe.db.get_value('GL Entry', {
'is_cancelled': 0,
'credit': ['>', 0],
'party': ['in', parties],
'fiscal_year': fiscal_year,
'company': inv.company,
'voucher_type': 'Sales Invoice',
}, 'sum(credit)') or 0.0
cumulative_threshold = tax_details.get('cumulative_threshold', 0)
current_invoice_total = get_invoice_total_without_tcs(inv, tax_details)
total_invoiced_amt = current_invoice_total + invoiced_amt + advance_amt - credit_note_amt
if ((cumulative_threshold and total_invoiced_amt >= cumulative_threshold)):
chargeable_amt = total_invoiced_amt - cumulative_threshold
tcs_amount = chargeable_amt * tax_details.rate / 100 if chargeable_amt > 0 else 0
return tcs_amount
def get_invoice_total_without_tcs(inv, tax_details):
tcs_tax_row = [d for d in inv.taxes if d.account_head == tax_details.account_head]
tcs_tax_row_amount = tcs_tax_row[0].base_tax_amount if tcs_tax_row else 0
return inv.grand_total - tcs_tax_row_amount
def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, posting_date, net_total):
tds_amount = 0
limit_consumed = frappe.db.get_value('Purchase Invoice', {
'supplier': ('in', parties),
'apply_tds': 1,
'docstatus': 1
}, 'sum(net_total)')
if is_valid_certificate(
ldc.valid_from, ldc.valid_upto,
posting_date, limit_consumed,
net_total, ldc.certificate_limit
):
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
return tds_amount
def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
_, year_start_date, year_end_date = fiscal_year_details
filters = {
'supplier': ['in', suppliers],
'is_return': 1,
'docstatus': 1,
'posting_date': ['between', (year_start_date, year_end_date)]
}
fields = ['abs(sum(net_total)) as net_total']
if company:
condition += "and company =%s" % (company)
if from_date and to_date:
condition += "and posting_date between %s and %s" % (from_date, to_date)
filters['company'] = company
## Appending the same supplier again if length of suppliers list is 1
## since tuple of single element list contains None, For example ('Test Supplier 1', )
## and the below query fails
if len(suppliers) == 1:
suppliers.append(suppliers[0])
return frappe.db.sql_list("""
select distinct voucher_no
from `tabGL Entry`
where party in %s and %s and debit > 0
and is_opening = 'No'
""", (tuple(suppliers), condition)) or []
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
condition = "and 1=1"
if company:
condition = " and company=%s " % company
if len(suppliers) == 1:
suppliers.append(suppliers[0])
return flt(frappe.db.sql("""
select abs(sum(net_total))
from `tabPurchase Invoice`
where supplier in %s and is_return=1 and docstatus=1
and posting_date between %s and %s %s
""", (tuple(suppliers), year_start_date, year_end_date, condition)))
return frappe.get_all('Purchase Invoice', filters, fields)[0].get('net_total') or 0.0
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
if current_amount < (certificate_limit - deducted_amount):
@ -227,4 +359,4 @@ def is_valid_certificate(valid_from, valid_upto, posting_date, deducted_amount,
certificate_limit > deducted_amount):
valid = True
return valid
return valid

View File

@ -9,7 +9,7 @@ from frappe.utils import today
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
test_dependencies = ["Supplier Group"]
test_dependencies = ["Supplier Group", "Customer Group"]
class TestTaxWithholdingCategory(unittest.TestCase):
@classmethod
@ -18,6 +18,9 @@ class TestTaxWithholdingCategory(unittest.TestCase):
create_records()
create_tax_with_holding_category()
def tearDown(self):
cancel_invoices()
def test_cumulative_threshold_tds(self):
frappe.db.set_value("Supplier", "Test TDS Supplier", "tax_withholding_category", "Cumulative Threshold TDS")
invoices = []
@ -128,9 +131,59 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in invoices:
d.cancel()
def test_cumulative_threshold_tcs(self):
frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS")
invoices = []
# create invoices for lower than single threshold tax rate
for _ in range(2):
si = create_sales_invoice(customer = "Test TCS Customer")
si.submit()
invoices.append(si)
# create another invoice whose total when added to previously created invoice,
# surpasses cumulative threshhold
si = create_sales_invoice(customer = "Test TCS Customer", rate=12000)
si.submit()
# assert tax collection on total invoice amount created until now
tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
self.assertEqual(tcs_charged, 200)
self.assertEqual(si.grand_total, 12200)
invoices.append(si)
# TCS is already collected once, so going forward system will collect TCS on every invoice
si = create_sales_invoice(customer = "Test TCS Customer", rate=5000)
si.submit()
tcs_charged = sum([d.base_tax_amount for d in si.taxes if d.account_head == 'TCS - _TC'])
self.assertEqual(tcs_charged, 500)
invoices.append(si)
#delete invoices to avoid clashing
for d in invoices:
d.cancel()
def cancel_invoices():
purchase_invoices = frappe.get_all("Purchase Invoice", {
'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
'docstatus': 1
}, pluck="name")
sales_invoices = frappe.get_all("Sales Invoice", {
'customer': 'Test TCS Customer',
'docstatus': 1
}, pluck="name")
for d in purchase_invoices:
frappe.get_doc('Purchase Invoice', d).cancel()
for d in sales_invoices:
frappe.get_doc('Sales Invoice', d).cancel()
def create_purchase_invoice(**args):
# return sales invoice doc object
item = frappe.get_doc('Item', {'item_name': 'TDS Item'})
item = frappe.db.get_value('Item', {'item_name': 'TDS Item'}, "name")
args = frappe._dict(args)
pi = frappe.get_doc({
@ -145,7 +198,7 @@ def create_purchase_invoice(**args):
"taxes": [],
"items": [{
'doctype': 'Purchase Invoice Item',
'item_code': item.name,
'item_code': item,
'qty': args.qty or 1,
'rate': args.rate or 10000,
'cost_center': 'Main - _TC',
@ -156,6 +209,33 @@ def create_purchase_invoice(**args):
pi.save()
return pi
def create_sales_invoice(**args):
# return sales invoice doc object
item = frappe.db.get_value('Item', {'item_name': 'TCS Item'}, "name")
args = frappe._dict(args)
si = frappe.get_doc({
"doctype": "Sales Invoice",
"posting_date": today(),
"customer": args.customer,
"company": '_Test Company',
"taxes_and_charges": "",
"currency": "INR",
"debit_to": "Debtors - _TC",
"taxes": [],
"items": [{
'doctype': 'Sales Invoice Item',
'item_code': item,
'qty': args.qty or 1,
'rate': args.rate or 10000,
'cost_center': 'Main - _TC',
'expense_account': 'Cost of Goods Sold - _TC'
}]
})
si.save()
return si
def create_records():
# create a new suppliers
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']:
@ -168,7 +248,17 @@ def create_records():
"doctype": "Supplier",
}).insert()
# create an item
for name in ['Test TCS Customer']:
if frappe.db.exists('Customer', name):
continue
frappe.get_doc({
"customer_group": "_Test Customer Group",
"customer_name": name,
"doctype": "Customer"
}).insert()
# create item
if not frappe.db.exists('Item', "TDS Item"):
frappe.get_doc({
"doctype": "Item",
@ -178,7 +268,16 @@ def create_records():
"is_stock_item": 0,
}).insert()
# create an account
if not frappe.db.exists('Item', "TCS Item"):
frappe.get_doc({
"doctype": "Item",
"item_code": "TCS Item",
"item_name": "TCS Item",
"item_group": "All Item Groups",
"is_stock_item": 1
}).insert()
# create tds account
if not frappe.db.exists("Account", "TDS - _TC"):
frappe.get_doc({
'doctype': 'Account',
@ -189,6 +288,17 @@ def create_records():
'root_type': 'Asset'
}).insert()
# create tcs account
if not frappe.db.exists("Account", "TCS - _TC"):
frappe.get_doc({
'doctype': 'Account',
'company': '_Test Company',
'account_name': 'TCS',
'parent_account': 'Duties and Taxes - _TC',
'report_type': 'Balance Sheet',
'root_type': 'Liability'
}).insert()
def create_tax_with_holding_category():
fiscal_year = get_fiscal_year(today(), company="_Test Company")[0]
@ -210,6 +320,23 @@ def create_tax_with_holding_category():
}]
}).insert()
if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
frappe.get_doc({
"doctype": "Tax Withholding Category",
"name": "Cumulative Threshold TCS",
"category_name": "10% TCS",
"rates": [{
'fiscal_year': fiscal_year,
'tax_withholding_rate': 10,
'single_threshold': 0,
'cumulative_threshold': 30000.00
}],
"accounts": [{
'company': '_Test Company',
'account': 'TCS - _TC'
}]
}).insert()
# Single thresold
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
frappe.get_doc({

View File

@ -196,7 +196,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
if not round_off_gle:
for k in ["voucher_type", "voucher_no", "company",
"posting_date", "remarks", "is_opening"]:
"posting_date", "remarks"]:
round_off_gle[k] = gl_map[0][k]
round_off_gle.update({
@ -208,6 +208,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
"cost_center": round_off_cost_center,
"party_type": None,
"party": None,
"is_opening": "No",
"against_voucher_type": None,
"against_voucher": None
})

View File

@ -13,7 +13,7 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0,
"is_complete": 0,
"modified": "2020-07-08 14:06:09.033880",
"modified": "2020-10-30 15:41:15.547225",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts",

View File

@ -1,19 +1,24 @@
{
"action": "Go to Page",
"action_label": "View Chart of Accounts",
"callback_message": "You can continue with the onboarding after exploring this page",
"callback_title": "Awesome Work",
"creation": "2020-05-13 19:58:20.928127",
"description": "# Chart Of Accounts\n\nThe Chart of Accounts is the blueprint of the accounts in your organization.\nIt is a tree view of the names of the Accounts (Ledgers and Groups) that a Company requires to manage its books of accounts. ERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to your needs and legal requirements.\n\nFor each company, Chart of Accounts signifies the way to classify the accounting entries, mostly\nbased on statutory (tax, compliance to government regulations) requirements.\n\nThere's a brief video tutorial about chart of accounts in the next step.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"intro_video_url": "https://www.youtube.com/embed/AcfMCT7wLLo",
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 17:40:28.410447",
"modified": "2020-10-30 14:35:59.474920",
"modified_by": "Administrator",
"name": "Chart of Accounts",
"owner": "Administrator",
"path": "Tree/Account",
"reference_document": "Account",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Review Chart of Accounts",
"validate_action": 0

View File

@ -1,18 +1,19 @@
{
"action": "Create Entry",
"action": "Show Form Tour",
"creation": "2020-05-14 17:53:00.876946",
"description": "# Account Settings\n\nThis is a crucial piece of configuration. There are various account settings in ERPNext to restrict and configure actions in the Accounting module.\n\nThe following settings are avaialble for you to configure\n\n1. Account Freezing \n2. Credit and Overbilling\n3. Invoicing and Tax Automations\n4. Balance Sheet configurations\n\nThere's much more, you can check it all out in this step",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 1,
"is_skipped": 0,
"modified": "2020-05-14 18:06:25.212923",
"modified": "2020-10-19 14:40:55.584484",
"modified_by": "Administrator",
"name": "Configure Account Settings",
"owner": "Administrator",
"reference_document": "Accounts Settings",
"show_form_tour": 0,
"show_full_form": 1,
"title": "Configure Account Settings",
"validate_action": 1

View File

@ -1,18 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:46:41.831517",
"description": "## Who is a Customer?\n\nA customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration.\n\nEvery customer needs to be assigned a unique id. Customer name itself can be the id or you can set a naming series for ids to be generated in Selling Settings.\n\nJust like the supplier, let's quickly create a customer.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-06-01 13:16:19.731719",
"modified": "2020-10-30 15:28:46.659660",
"modified_by": "Administrator",
"name": "Create a Customer",
"owner": "Administrator",
"reference_document": "Customer",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Customer",
"validate_action": 1

View File

@ -1,19 +1,21 @@
{
"action": "Create Entry",
"creation": "2020-05-12 18:16:06.624554",
"description": "## Products and Services\n\nDepending on the nature of your business, you might be selling products or services to your clients or even both. \nERPNext is optimized for itemized management of your sales and purchase.\n\nThe **Item Master** is where you can add all your sales items. If you are in services, you can create an Item for each service that you offer. If you run a manufacturing business, the same master is used for keeping a record of raw materials, sub-assemblies etc.\n\nCompleting the Item Master is very essential for the successful implementation of ERPNext. We have a brief video introducing the item master for you, you can watch it in the next step.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"intro_video_url": "https://www.youtube.com/watch?v=Sl5UFA5H5EQ",
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-12 18:30:02.489949",
"modified": "2020-10-30 15:20:30.133495",
"modified_by": "Administrator",
"name": "Create a Product",
"owner": "Administrator",
"reference_document": "Item",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Product",
"title": "Create a Sales Item",
"validate_action": 1
}

View File

@ -1,18 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 22:09:10.043554",
"description": "## Who is a Supplier?\n\nSuppliers are companies or individuals who provide you with products or services. ERPNext has comprehensive features for purchase cycles. \n\nLet's quickly create a supplier with the minimal details required. You need the name of the supplier, assign the supplier to a group, and select the type of the supplier, viz. Company or Individual.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 22:09:10.043554",
"modified": "2020-10-30 15:26:48.315772",
"modified_by": "Administrator",
"name": "Create a Supplier",
"owner": "Administrator",
"reference_document": "Supplier",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Supplier",
"validate_action": 1

View File

@ -1,18 +1,19 @@
{
"action": "Create Entry",
"creation": "2020-05-14 22:10:07.049704",
"description": "# What's a Purchase Invoice?\n\nA Purchase Invoice is a bill you receive from your Suppliers against which you need to make the payment.\nPurchase Invoice is the exact opposite of your Sales Invoice. Here you accrue expenses to your Supplier. \n\nThe following is what a typical purchase cycle looks like, however you can create a purchase invoice directly as well.\n\n![Purchase Flow](https://docs.erpnext.com/docs/assets/img/accounts/pi-flow.png)\n\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_mandatory": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-05-14 22:10:07.049704",
"modified": "2020-10-30 15:30:26.337773",
"modified_by": "Administrator",
"name": "Create Your First Purchase Invoice",
"owner": "Administrator",
"reference_document": "Purchase Invoice",
"show_form_tour": 0,
"show_full_form": 1,
"title": "Create Your First Purchase Invoice ",
"validate_action": 1

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