mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-09 08:11:19 +00:00
Merge branch 'develop' of https://github.com/frappe/erpnext into enconnex_erpnext
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
ERPNext as a monolith includes the following areas for managing businesses:
|
ERPNext as a monolith includes the following areas for managing businesses:
|
||||||
|
|
||||||
1. [Accounting](https://erpnext.com/open-source-accounting)
|
1. [Accounting](https://erpnext.com/open-source-accounting)
|
||||||
1. [Inventory](https://erpnext.com/distribution/inventory-management-system)
|
1. [Warehouse Management](https://erpnext.com/distribution/warehouse-management-system)
|
||||||
1. [CRM](https://erpnext.com/open-source-crm)
|
1. [CRM](https://erpnext.com/open-source-crm)
|
||||||
1. [Sales](https://erpnext.com/open-source-sales-purchase)
|
1. [Sales](https://erpnext.com/open-source-sales-purchase)
|
||||||
1. [Purchase](https://erpnext.com/open-source-sales-purchase)
|
1. [Purchase](https://erpnext.com/open-source-sales-purchase)
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Goods and Services Tax (GST India)",
|
"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]"
|
"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,
|
"hidden": 0,
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Accounting",
|
"label": "Accounting",
|
||||||
"modified": "2020-06-19 12:42:44.054598",
|
"modified": "2020-10-08 20:31:46.022470",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting",
|
"name": "Accounting",
|
||||||
@@ -147,11 +147,6 @@
|
|||||||
"link_to": "Trial Balance",
|
"link_to": "Trial Balance",
|
||||||
"type": "Report"
|
"type": "Report"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"label": "Point of Sale",
|
|
||||||
"link_to": "point-of-sale",
|
|
||||||
"type": "Page"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "Dashboard",
|
"label": "Dashboard",
|
||||||
"link_to": "Accounts",
|
"link_to": "Accounts",
|
||||||
|
|||||||
@@ -117,7 +117,9 @@ class Account(NestedSet):
|
|||||||
|
|
||||||
for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
|
for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
|
||||||
parent_acc_name_map[d["company"]] = d["name"]
|
parent_acc_name_map[d["company"]] = d["name"]
|
||||||
|
|
||||||
if not parent_acc_name_map: return
|
if not parent_acc_name_map: return
|
||||||
|
|
||||||
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
||||||
|
|
||||||
def validate_group_or_ledger(self):
|
def validate_group_or_ledger(self):
|
||||||
@@ -289,10 +291,30 @@ def validate_account_number(name, account_number, company):
|
|||||||
.format(account_number, account_with_same_number))
|
.format(account_number, account_with_same_number))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_account_number(name, account_name, account_number=None):
|
def update_account_number(name, account_name, account_number=None, from_descendant=False):
|
||||||
|
|
||||||
account = frappe.db.get_value("Account", name, "company", as_dict=True)
|
account = frappe.db.get_value("Account", name, "company", as_dict=True)
|
||||||
if not account: return
|
if not account: return
|
||||||
|
|
||||||
|
old_acc_name, old_acc_number = frappe.db.get_value('Account', name, \
|
||||||
|
["account_name", "account_number"])
|
||||||
|
|
||||||
|
# check if account exists in parent company
|
||||||
|
ancestors = get_ancestors_of("Company", account.company)
|
||||||
|
allow_independent_account_creation = frappe.get_value("Company", account.company, "allow_account_creation_against_child_company")
|
||||||
|
|
||||||
|
if ancestors and not allow_independent_account_creation:
|
||||||
|
for ancestor in ancestors:
|
||||||
|
if frappe.db.get_value("Account", {'account_name': old_acc_name, 'company': ancestor}, 'name'):
|
||||||
|
# same account in parent company exists
|
||||||
|
allow_child_account_creation = _("Allow Account Creation Against Child Company")
|
||||||
|
|
||||||
|
message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor))
|
||||||
|
message += "<br>" + _("Renaming it is only allowed via parent company {0}, \
|
||||||
|
to avoid mismatch.").format(frappe.bold(ancestor)) + "<br><br>"
|
||||||
|
message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company))
|
||||||
|
|
||||||
|
frappe.throw(message, title=_("Rename Not Allowed"))
|
||||||
|
|
||||||
validate_account_number(name, account_number, account.company)
|
validate_account_number(name, account_number, account.company)
|
||||||
if account_number:
|
if account_number:
|
||||||
frappe.db.set_value("Account", name, "account_number", account_number.strip())
|
frappe.db.set_value("Account", name, "account_number", account_number.strip())
|
||||||
@@ -300,6 +322,12 @@ def update_account_number(name, account_name, account_number=None):
|
|||||||
frappe.db.set_value("Account", name, "account_number", "")
|
frappe.db.set_value("Account", name, "account_number", "")
|
||||||
frappe.db.set_value("Account", name, "account_name", account_name.strip())
|
frappe.db.set_value("Account", name, "account_name", account_name.strip())
|
||||||
|
|
||||||
|
if not from_descendant:
|
||||||
|
# Update and rename in child company accounts as well
|
||||||
|
descendants = get_descendants_of('Company', account.company)
|
||||||
|
if descendants:
|
||||||
|
sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number, old_acc_number)
|
||||||
|
|
||||||
new_name = get_account_autoname(account_number, account_name, account.company)
|
new_name = get_account_autoname(account_number, account_name, account.company)
|
||||||
if name != new_name:
|
if name != new_name:
|
||||||
frappe.rename_doc("Account", name, new_name, force=1)
|
frappe.rename_doc("Account", name, new_name, force=1)
|
||||||
@@ -330,3 +358,14 @@ def get_root_company(company):
|
|||||||
# return the topmost company in the hierarchy
|
# return the topmost company in the hierarchy
|
||||||
ancestors = get_ancestors_of('Company', company, "lft asc")
|
ancestors = get_ancestors_of('Company', company, "lft asc")
|
||||||
return [ancestors[0]] if ancestors else []
|
return [ancestors[0]] if ancestors else []
|
||||||
|
|
||||||
|
def sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number=None, old_acc_number=None):
|
||||||
|
filters = {
|
||||||
|
"company": ["in", descendants],
|
||||||
|
"account_name": old_acc_name,
|
||||||
|
}
|
||||||
|
if old_acc_number:
|
||||||
|
filters["account_number"] = old_acc_number
|
||||||
|
|
||||||
|
for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
|
||||||
|
update_account_number(d["name"], account_name, account_number, from_descendant=True)
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ def build_tree_from_json(chart_template, chart_data=None):
|
|||||||
|
|
||||||
account['parent_account'] = parent
|
account['parent_account'] = parent
|
||||||
account['expandable'] = True if identify_is_group(child) else False
|
account['expandable'] = True if identify_is_group(child) else False
|
||||||
account['value'] = (child.get('account_number') + ' - ' + account_name) \
|
account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
|
||||||
if child.get('account_number') else account_name
|
if child.get('account_number') else account_name
|
||||||
accounts.append(account)
|
accounts.append(account)
|
||||||
_import_accounts(child, account['value'])
|
_import_accounts(child, account['value'])
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import unittest
|
import unittest
|
||||||
import frappe
|
import frappe
|
||||||
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
|
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
|
||||||
from erpnext.accounts.doctype.account.account import update_account_number
|
from erpnext.accounts.doctype.account.account import update_account_number, merge_account
|
||||||
from erpnext.accounts.doctype.account.account import merge_account
|
|
||||||
|
|
||||||
class TestAccount(unittest.TestCase):
|
class TestAccount(unittest.TestCase):
|
||||||
def test_rename_account(self):
|
def test_rename_account(self):
|
||||||
@@ -99,7 +98,8 @@ class TestAccount(unittest.TestCase):
|
|||||||
"Softwares - _TC", doc.is_group, doc.root_type, doc.company)
|
"Softwares - _TC", doc.is_group, doc.root_type, doc.company)
|
||||||
|
|
||||||
def test_account_sync(self):
|
def test_account_sync(self):
|
||||||
del frappe.local.flags["ignore_root_company_validation"]
|
frappe.local.flags.pop("ignore_root_company_validation", None)
|
||||||
|
|
||||||
acc = frappe.new_doc("Account")
|
acc = frappe.new_doc("Account")
|
||||||
acc.account_name = "Test Sync Account"
|
acc.account_name = "Test Sync Account"
|
||||||
acc.parent_account = "Temporary Accounts - _TC3"
|
acc.parent_account = "Temporary Accounts - _TC3"
|
||||||
@@ -111,6 +111,55 @@ class TestAccount(unittest.TestCase):
|
|||||||
self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
|
self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
|
||||||
self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
|
self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
|
||||||
|
|
||||||
|
def test_account_rename_sync(self):
|
||||||
|
frappe.local.flags.pop("ignore_root_company_validation", None)
|
||||||
|
|
||||||
|
acc = frappe.new_doc("Account")
|
||||||
|
acc.account_name = "Test Rename Account"
|
||||||
|
acc.parent_account = "Temporary Accounts - _TC3"
|
||||||
|
acc.company = "_Test Company 3"
|
||||||
|
acc.insert()
|
||||||
|
|
||||||
|
# Rename account in parent company
|
||||||
|
update_account_number(acc.name, "Test Rename Sync Account", "1234")
|
||||||
|
|
||||||
|
# Check if renamed in children
|
||||||
|
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 4", "account_number": "1234"}))
|
||||||
|
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 5", "account_number": "1234"}))
|
||||||
|
|
||||||
|
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC3")
|
||||||
|
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC4")
|
||||||
|
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC5")
|
||||||
|
|
||||||
|
def test_child_company_account_rename_sync(self):
|
||||||
|
frappe.local.flags.pop("ignore_root_company_validation", None)
|
||||||
|
|
||||||
|
acc = frappe.new_doc("Account")
|
||||||
|
acc.account_name = "Test Group Account"
|
||||||
|
acc.parent_account = "Temporary Accounts - _TC3"
|
||||||
|
acc.is_group = 1
|
||||||
|
acc.company = "_Test Company 3"
|
||||||
|
acc.insert()
|
||||||
|
|
||||||
|
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Group Account", "company": "_Test Company 4"}))
|
||||||
|
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Group Account", "company": "_Test Company 5"}))
|
||||||
|
|
||||||
|
# Try renaming child company account
|
||||||
|
acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Group Account", "company": "_Test Company 5"})
|
||||||
|
self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
|
||||||
|
|
||||||
|
# Rename child company account with allow_account_creation_against_child_company enabled
|
||||||
|
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
|
||||||
|
|
||||||
|
update_account_number(acc_tc_5, "Test Modified Account")
|
||||||
|
self.assertTrue(frappe.db.exists("Account", {'name': "Test Modified Account - _TC5", "company": "_Test Company 5"}))
|
||||||
|
|
||||||
|
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
|
||||||
|
|
||||||
|
to_delete = ["Test Group Account - _TC3", "Test Group Account - _TC4", "Test Modified Account - _TC5"]
|
||||||
|
for doc in to_delete:
|
||||||
|
frappe.delete_doc("Account", doc)
|
||||||
|
|
||||||
def _make_test_records(verbose):
|
def _make_test_records(verbose):
|
||||||
from frappe.test_runner import make_test_objects
|
from frappe.test_runner import make_test_objects
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
"default": "1",
|
"default": "1",
|
||||||
"fieldname": "unlink_advance_payment_on_cancelation_of_order",
|
"fieldname": "unlink_advance_payment_on_cancelation_of_order",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Unlink Advance Payment on Cancelation of Order"
|
"label": "Unlink Advance Payment on Cancellation of Order"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
@@ -223,9 +223,10 @@
|
|||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-03 20:13:26.043092",
|
"modified": "2020-10-07 14:58:50.325577",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
|||||||
@@ -27,4 +27,4 @@ def get_vouchar_detials(column_list, doctype, docname):
|
|||||||
for col in column_list:
|
for col in column_list:
|
||||||
sanitize_searchfield(col)
|
sanitize_searchfield(col)
|
||||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class BankStatementTransactionEntry(Document):
|
|||||||
|
|
||||||
def populate_payment_entries(self):
|
def populate_payment_entries(self):
|
||||||
if self.bank_statement is None: return
|
if self.bank_statement is None: return
|
||||||
filename = self.bank_statement.split("/")[-1]
|
file_url = self.bank_statement
|
||||||
if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
|
if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0):
|
||||||
frappe.throw(_("Transactions already retreived from the statement"))
|
frappe.throw(_("Transactions already retreived from the statement"))
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class BankStatementTransactionEntry(Document):
|
|||||||
if self.bank_settings:
|
if self.bank_settings:
|
||||||
mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
|
mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items
|
||||||
statement_headers = self.get_statement_headers()
|
statement_headers = self.get_statement_headers()
|
||||||
transactions = get_transaction_entries(filename, statement_headers)
|
transactions = get_transaction_entries(file_url, statement_headers)
|
||||||
for entry in transactions:
|
for entry in transactions:
|
||||||
date = entry[statement_headers["Date"]].strip()
|
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"]))
|
#print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"]))
|
||||||
@@ -398,20 +398,21 @@ def get_transaction_info(headers, header_index, row):
|
|||||||
transaction[header] = ""
|
transaction[header] = ""
|
||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
def get_transaction_entries(filename, headers):
|
def get_transaction_entries(file_url, headers):
|
||||||
header_index = {}
|
header_index = {}
|
||||||
rows, transactions = [], []
|
rows, transactions = [], []
|
||||||
|
|
||||||
if (filename.lower().endswith("xlsx")):
|
if (file_url.lower().endswith("xlsx")):
|
||||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||||
rows = read_xlsx_file_from_attached_file(file_id=filename)
|
rows = read_xlsx_file_from_attached_file(file_url=file_url)
|
||||||
elif (filename.lower().endswith("csv")):
|
elif (file_url.lower().endswith("csv")):
|
||||||
from frappe.utils.csvutils import read_csv_content
|
from frappe.utils.csvutils import read_csv_content
|
||||||
_file = frappe.get_doc("File", {"file_name": filename})
|
_file = frappe.get_doc("File", {"file_url": file_url})
|
||||||
filepath = _file.get_full_path()
|
filepath = _file.get_full_path()
|
||||||
with open(filepath,'rb') as csvfile:
|
with open(filepath,'rb') as csvfile:
|
||||||
rows = read_csv_content(csvfile.read())
|
rows = read_csv_content(csvfile.read())
|
||||||
elif (filename.lower().endswith("xls")):
|
elif (file_url.lower().endswith("xls")):
|
||||||
|
filename = file_url.split("/")[-1]
|
||||||
rows = get_rows_from_xls_file(filename)
|
rows = get_rows_from_xls_file(filename)
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Only .csv and .xlsx files are supported currently"))
|
frappe.throw(_("Only .csv and .xlsx files are supported currently"))
|
||||||
|
|||||||
@@ -91,15 +91,11 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
|
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)
|
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
|
||||||
|
|
||||||
def add_transactions():
|
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
||||||
if frappe.flags.test_bank_transactions_created:
|
|
||||||
return
|
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Bank",
|
"doctype": "Bank",
|
||||||
"bank_name":"Citi Bank",
|
"bank_name":bank_name,
|
||||||
}).insert()
|
}).insert()
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
@@ -108,12 +104,19 @@ def add_transactions():
|
|||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Bank Account",
|
"doctype": "Bank Account",
|
||||||
"account_name":"Checking Account",
|
"account_name":"Checking Account",
|
||||||
"bank": "Citi Bank",
|
"bank": bank_name,
|
||||||
"account": "_Test Bank - _TC"
|
"account": account_name
|
||||||
}).insert()
|
}).insert()
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def add_transactions():
|
||||||
|
if frappe.flags.test_bank_transactions_created:
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
create_bank_account()
|
||||||
|
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
"doctype": "Bank Transaction",
|
"doctype": "Bank Transaction",
|
||||||
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
|
||||||
|
|||||||
@@ -1,785 +1,204 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-05-16 11:42:29.632528",
|
"creation": "2016-05-16 11:42:29.632528",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"budget_against",
|
||||||
|
"company",
|
||||||
|
"cost_center",
|
||||||
|
"project",
|
||||||
|
"fiscal_year",
|
||||||
|
"column_break_3",
|
||||||
|
"monthly_distribution",
|
||||||
|
"amended_from",
|
||||||
|
"section_break_6",
|
||||||
|
"applicable_on_material_request",
|
||||||
|
"action_if_annual_budget_exceeded_on_mr",
|
||||||
|
"action_if_accumulated_monthly_budget_exceeded_on_mr",
|
||||||
|
"column_break_13",
|
||||||
|
"applicable_on_purchase_order",
|
||||||
|
"action_if_annual_budget_exceeded_on_po",
|
||||||
|
"action_if_accumulated_monthly_budget_exceeded_on_po",
|
||||||
|
"section_break_16",
|
||||||
|
"applicable_on_booking_actual_expenses",
|
||||||
|
"action_if_annual_budget_exceeded",
|
||||||
|
"action_if_accumulated_monthly_budget_exceeded",
|
||||||
|
"section_break_21",
|
||||||
|
"accounts"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Cost Center",
|
"default": "Cost Center",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "budget_against",
|
"fieldname": "budget_against",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Budget Against",
|
"label": "Budget Against",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nCost Center\nProject",
|
"options": "\nCost Center\nProject",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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,
|
|
||||||
"depends_on": "eval:doc.budget_against == 'Cost Center'",
|
"depends_on": "eval:doc.budget_against == 'Cost Center'",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "cost_center",
|
"fieldname": "cost_center",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Cost Center",
|
"label": "Cost Center",
|
||||||
"length": 0,
|
"options": "Cost Center"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Cost Center",
|
|
||||||
"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.budget_against == 'Project'",
|
"depends_on": "eval:doc.budget_against == 'Project'",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
"fieldtype": "Link",
|
"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,
|
"in_standard_filter": 1,
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"length": 0,
|
"options": "Project"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Project",
|
|
||||||
"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": "fiscal_year",
|
"fieldname": "fiscal_year",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Fiscal Year",
|
"label": "Fiscal Year",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"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,
|
|
||||||
"depends_on": "eval:in_list([\"Stop\", \"Warn\"], doc.action_if_accumulated_monthly_budget_exceeded_on_po || doc.action_if_accumulated_monthly_budget_exceeded_on_mr || doc.action_if_accumulated_monthly_budget_exceeded_on_actual)",
|
"depends_on": "eval:in_list([\"Stop\", \"Warn\"], doc.action_if_accumulated_monthly_budget_exceeded_on_po || doc.action_if_accumulated_monthly_budget_exceeded_on_mr || doc.action_if_accumulated_monthly_budget_exceeded_on_actual)",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "monthly_distribution",
|
"fieldname": "monthly_distribution",
|
||||||
"fieldtype": "Link",
|
"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": "Monthly Distribution",
|
"label": "Monthly Distribution",
|
||||||
"length": 0,
|
"options": "Monthly Distribution"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Monthly Distribution",
|
|
||||||
"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": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Budget",
|
"options": "Budget",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"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,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_6",
|
"fieldname": "section_break_6",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Control Action"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Control Action",
|
|
||||||
"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,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "applicable_on_material_request",
|
"fieldname": "applicable_on_material_request",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Applicable on Material Request"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Applicable on Material Request",
|
|
||||||
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Stop",
|
"default": "Stop",
|
||||||
"depends_on": "eval:doc.applicable_on_material_request == 1",
|
"depends_on": "eval:doc.applicable_on_material_request == 1",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "action_if_annual_budget_exceeded_on_mr",
|
"fieldname": "action_if_annual_budget_exceeded_on_mr",
|
||||||
"fieldtype": "Select",
|
"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": "Action if Annual Budget Exceeded on MR",
|
"label": "Action if Annual Budget Exceeded on MR",
|
||||||
"length": 0,
|
"options": "\nStop\nWarn\nIgnore"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStop\nWarn\nIgnore",
|
|
||||||
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Warn",
|
"default": "Warn",
|
||||||
"depends_on": "eval:doc.applicable_on_material_request == 1",
|
"depends_on": "eval:doc.applicable_on_material_request == 1",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "action_if_accumulated_monthly_budget_exceeded_on_mr",
|
"fieldname": "action_if_accumulated_monthly_budget_exceeded_on_mr",
|
||||||
"fieldtype": "Select",
|
"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": "Action if Accumulated Monthly Budget Exceeded on MR",
|
"label": "Action if Accumulated Monthly Budget Exceeded on MR",
|
||||||
"length": 0,
|
"options": "\nStop\nWarn\nIgnore"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStop\nWarn\nIgnore",
|
|
||||||
"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_13",
|
"fieldname": "column_break_13",
|
||||||
"fieldtype": "Column Break",
|
"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,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "applicable_on_purchase_order",
|
"fieldname": "applicable_on_purchase_order",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Applicable on Purchase Order"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Applicable on Purchase Order",
|
|
||||||
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Stop",
|
"default": "Stop",
|
||||||
"depends_on": "eval:doc.applicable_on_purchase_order == 1",
|
"depends_on": "eval:doc.applicable_on_purchase_order == 1",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "action_if_annual_budget_exceeded_on_po",
|
"fieldname": "action_if_annual_budget_exceeded_on_po",
|
||||||
"fieldtype": "Select",
|
"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": "Action if Annual Budget Exceeded on PO",
|
"label": "Action if Annual Budget Exceeded on PO",
|
||||||
"length": 0,
|
"options": "\nStop\nWarn\nIgnore"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStop\nWarn\nIgnore",
|
|
||||||
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Warn",
|
"default": "Warn",
|
||||||
"depends_on": "eval:doc.applicable_on_purchase_order == 1",
|
"depends_on": "eval:doc.applicable_on_purchase_order == 1",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "action_if_accumulated_monthly_budget_exceeded_on_po",
|
"fieldname": "action_if_accumulated_monthly_budget_exceeded_on_po",
|
||||||
"fieldtype": "Select",
|
"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": "Action if Accumulated Monthly Budget Exceeded on PO",
|
"label": "Action if Accumulated Monthly Budget Exceeded on PO",
|
||||||
"length": 0,
|
"options": "\nStop\nWarn\nIgnore"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStop\nWarn\nIgnore",
|
|
||||||
"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": "section_break_16",
|
"fieldname": "section_break_16",
|
||||||
"fieldtype": "Section Break",
|
"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,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "applicable_on_booking_actual_expenses",
|
"fieldname": "applicable_on_booking_actual_expenses",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Applicable on booking actual expenses"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Applicable on booking actual expenses",
|
|
||||||
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Stop",
|
"default": "Stop",
|
||||||
"depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1",
|
"depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "action_if_annual_budget_exceeded",
|
"fieldname": "action_if_annual_budget_exceeded",
|
||||||
"fieldtype": "Select",
|
"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": "Action if Annual Budget Exceeded on Actual",
|
"label": "Action if Annual Budget Exceeded on Actual",
|
||||||
"length": 0,
|
"options": "\nStop\nWarn\nIgnore"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStop\nWarn\nIgnore",
|
|
||||||
"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": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Warn",
|
"default": "Warn",
|
||||||
"depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1",
|
"depends_on": "eval:doc.applicable_on_booking_actual_expenses == 1",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "action_if_accumulated_monthly_budget_exceeded",
|
"fieldname": "action_if_accumulated_monthly_budget_exceeded",
|
||||||
"fieldtype": "Select",
|
"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": "Action if Accumulated Monthly Budget Exceeded on Actual",
|
"label": "Action if Accumulated Monthly Budget Exceeded on Actual",
|
||||||
"length": 0,
|
"options": "\nStop\nWarn\nIgnore"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nStop\nWarn\nIgnore",
|
|
||||||
"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": "section_break_21",
|
"fieldname": "section_break_21",
|
||||||
"fieldtype": "Section Break",
|
"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": "",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "accounts",
|
"fieldname": "accounts",
|
||||||
"fieldtype": "Table",
|
"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": "Budget Accounts",
|
"label": "Budget Accounts",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Budget Account",
|
"options": "Budget Account",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"links": [],
|
||||||
"istable": 0,
|
"modified": "2020-10-06 15:13:54.055854",
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-03-22 12:06:02.323099",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Budget",
|
"name": "Budget",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -789,26 +208,17 @@
|
|||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 1,
|
"import": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ var create_import_button = function(frm) {
|
|||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if(!r.exc) {
|
if(!r.exc) {
|
||||||
clearInterval(frm.page["interval"]);
|
clearInterval(frm.page["interval"]);
|
||||||
frm.page.set_indicator(__('Import Successfull'), 'blue');
|
frm.page.set_indicator(__('Import Successful'), 'blue');
|
||||||
create_reset_button(frm);
|
create_reset_button(frm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
|||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
from frappe.test_runner import make_test_objects
|
from frappe.test_runner import make_test_objects
|
||||||
|
|
||||||
|
test_dependencies = ['Item']
|
||||||
|
|
||||||
def test_create_test_data():
|
def test_create_test_data():
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
# create test item
|
# create test item
|
||||||
@@ -95,7 +97,6 @@ def test_create_test_data():
|
|||||||
})
|
})
|
||||||
coupon_code.insert()
|
coupon_code.insert()
|
||||||
|
|
||||||
|
|
||||||
class TestCouponCode(unittest.TestCase):
|
class TestCouponCode(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
test_create_test_data()
|
test_create_test_data()
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ def resolve_dunning(doc, state):
|
|||||||
|
|
||||||
def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
|
def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
|
||||||
interest_amount = 0
|
interest_amount = 0
|
||||||
|
grand_total = 0
|
||||||
if rate_of_interest:
|
if rate_of_interest:
|
||||||
interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100
|
interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100
|
||||||
interest_amount = (interest_per_year * cint(overdue_days)) / 365
|
interest_amount = (interest_per_year * cint(overdue_days)) / 365
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('References'),
|
'label': _('References'),
|
||||||
'items': ['Period Closing Voucher', 'Request for Quotation', 'Tax Withholding Category']
|
'items': ['Period Closing Voucher', 'Tax Withholding Category']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Target Details'),
|
'label': _('Target Details'),
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2020-06-18 20:27:42.615842",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "ahmad@havenir.com",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Item Tax Template",
|
"name": "Item Tax Template",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
|||||||
@@ -638,20 +638,12 @@ $.extend(erpnext.journal_entry, {
|
|||||||
return { filters: filters };
|
return { filters: filters };
|
||||||
},
|
},
|
||||||
|
|
||||||
reverse_journal_entry: function(frm) {
|
reverse_journal_entry: function() {
|
||||||
var me = frm.doc;
|
frappe.model.open_mapped_doc({
|
||||||
for(var i=0; i<me.accounts.length; i++) {
|
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
|
||||||
me.accounts[i].credit += me.accounts[i].debit;
|
frm: cur_frm
|
||||||
me.accounts[i].debit = me.accounts[i].credit - me.accounts[i].debit;
|
})
|
||||||
me.accounts[i].credit -= me.accounts[i].debit;
|
},
|
||||||
me.accounts[i].credit_in_account_currency = me.accounts[i].credit;
|
|
||||||
me.accounts[i].debit_in_account_currency = me.accounts[i].debit;
|
|
||||||
me.accounts[i].reference_type = "Journal Entry";
|
|
||||||
me.accounts[i].reference_name = me.name
|
|
||||||
}
|
|
||||||
frm.copy_doc();
|
|
||||||
cur_frm.reload_doc();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$.extend(erpnext.journal_entry, {
|
$.extend(erpnext.journal_entry, {
|
||||||
|
|||||||
@@ -22,8 +22,12 @@ class JournalEntry(AccountsController):
|
|||||||
return self.voucher_type
|
return self.voucher_type
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
if self.voucher_type == 'Opening Entry':
|
||||||
|
self.is_opening = 'Yes'
|
||||||
|
|
||||||
if not self.is_opening:
|
if not self.is_opening:
|
||||||
self.is_opening='No'
|
self.is_opening='No'
|
||||||
|
|
||||||
self.clearance_date = None
|
self.clearance_date = None
|
||||||
|
|
||||||
self.validate_party()
|
self.validate_party()
|
||||||
@@ -1021,3 +1025,34 @@ def make_inter_company_journal_entry(name, voucher_type, company):
|
|||||||
journal_entry.posting_date = nowdate()
|
journal_entry.posting_date = nowdate()
|
||||||
journal_entry.inter_company_journal_entry_reference = name
|
journal_entry.inter_company_journal_entry_reference = name
|
||||||
return journal_entry.as_dict()
|
return journal_entry.as_dict()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_reverse_journal_entry(source_name, target_doc=None):
|
||||||
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
|
def update_accounts(source, target, source_parent):
|
||||||
|
target.reference_type = "Journal Entry"
|
||||||
|
target.reference_name = source_parent.name
|
||||||
|
|
||||||
|
doclist = get_mapped_doc("Journal Entry", source_name, {
|
||||||
|
"Journal Entry": {
|
||||||
|
"doctype": "Journal Entry",
|
||||||
|
"validation": {
|
||||||
|
"docstatus": ["=", 1]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Journal Entry Account": {
|
||||||
|
"doctype": "Journal Entry Account",
|
||||||
|
"field_map": {
|
||||||
|
"account_currency": "account_currency",
|
||||||
|
"exchange_rate": "exchange_rate",
|
||||||
|
"debit_in_account_currency": "credit_in_account_currency",
|
||||||
|
"debit": "credit",
|
||||||
|
"credit_in_account_currency": "debit_in_account_currency",
|
||||||
|
"credit": "debit",
|
||||||
|
},
|
||||||
|
"postprocess": update_accounts,
|
||||||
|
},
|
||||||
|
}, target_doc)
|
||||||
|
|
||||||
|
return doclist
|
||||||
@@ -167,6 +167,49 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertFalse(gle)
|
self.assertFalse(gle)
|
||||||
|
|
||||||
|
def test_reverse_journal_entry(self):
|
||||||
|
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
|
||||||
|
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||||
|
"Sales - _TC", 100, exchange_rate=50, save=False)
|
||||||
|
|
||||||
|
jv.get("accounts")[1].credit_in_account_currency = 5000
|
||||||
|
jv.get("accounts")[1].exchange_rate = 1
|
||||||
|
jv.submit()
|
||||||
|
|
||||||
|
rjv = make_reverse_journal_entry(jv.name)
|
||||||
|
rjv.posting_date = nowdate()
|
||||||
|
rjv.submit()
|
||||||
|
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||||
|
debit_in_account_currency, credit_in_account_currency
|
||||||
|
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
|
||||||
|
order by account asc""", rjv.name, as_dict=1)
|
||||||
|
|
||||||
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
|
|
||||||
|
expected_values = {
|
||||||
|
"_Test Bank USD - _TC": {
|
||||||
|
"account_currency": "USD",
|
||||||
|
"debit": 0,
|
||||||
|
"debit_in_account_currency": 0,
|
||||||
|
"credit": 5000,
|
||||||
|
"credit_in_account_currency": 100,
|
||||||
|
},
|
||||||
|
"Sales - _TC": {
|
||||||
|
"account_currency": "INR",
|
||||||
|
"debit": 5000,
|
||||||
|
"debit_in_account_currency": 5000,
|
||||||
|
"credit": 0,
|
||||||
|
"credit_in_account_currency": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEqual(expected_values[gle.account][field], gle[field])
|
||||||
|
|
||||||
def test_disallow_change_in_account_currency_for_a_party(self):
|
def test_disallow_change_in_account_currency_for_a_party(self):
|
||||||
# create jv in USD
|
# create jv in USD
|
||||||
jv = make_journal_entry("_Test Bank USD - _TC",
|
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||||
|
|||||||
@@ -45,11 +45,11 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-credit-card",
|
"icon": "fa fa-credit-card",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-08-14 14:58:42.079115",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "sammish.thundiyil@gmail.com",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Mode of Payment",
|
"name": "Mode of Payment",
|
||||||
"owner": "harshada@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"create": 1,
|
"create": 1,
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("paid_from", function() {
|
frm.set_query("paid_from", function() {
|
||||||
|
frm.events.validate_company(frm);
|
||||||
|
|
||||||
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
|
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
|
||||||
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"account_type": ["in", account_types],
|
"account_type": ["in", account_types],
|
||||||
@@ -23,29 +24,35 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("party_type", function() {
|
frm.set_query("party_type", function() {
|
||||||
|
frm.events.validate_company(frm);
|
||||||
return{
|
return{
|
||||||
"filters": {
|
filters: {
|
||||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("party_bank_account", function() {
|
frm.set_query("party_bank_account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"is_company_account":0,
|
is_company_account: 0,
|
||||||
party_type: frm.doc.party_type,
|
party_type: frm.doc.party_type,
|
||||||
party: frm.doc.party
|
party: frm.doc.party
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("bank_account", function() {
|
frm.set_query("bank_account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"is_company_account":1
|
is_company_account: 1,
|
||||||
|
company: frm.doc.company
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("contact_person", function() {
|
frm.set_query("contact_person", function() {
|
||||||
if (frm.doc.party) {
|
if (frm.doc.party) {
|
||||||
return {
|
return {
|
||||||
@@ -57,10 +64,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("paid_to", function() {
|
frm.set_query("paid_to", function() {
|
||||||
|
frm.events.validate_company(frm);
|
||||||
|
|
||||||
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
|
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
|
||||||
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"account_type": ["in", account_types],
|
"account_type": ["in", account_types],
|
||||||
@@ -149,6 +158,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.show_general_ledger(frm);
|
frm.events.show_general_ledger(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
validate_company: (frm) => {
|
||||||
|
if (!frm.doc.company){
|
||||||
|
frappe.throw({message:__("Please select a Company first."), title: __("Mandatory")});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
company: function(frm) {
|
company: function(frm) {
|
||||||
frm.events.hide_unhide_fields(frm);
|
frm.events.hide_unhide_fields(frm);
|
||||||
frm.events.set_dynamic_labels(frm);
|
frm.events.set_dynamic_labels(frm);
|
||||||
@@ -342,7 +357,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
() => {
|
() => {
|
||||||
frm.set_party_account_based_on_party = false;
|
frm.set_party_account_based_on_party = false;
|
||||||
if (r.message.bank_account) {
|
if (r.message.bank_account) {
|
||||||
frm.set_value("party_bank_account", r.message.bank_account);
|
frm.set_value("bank_account", r.message.bank_account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2016-06-01 14:38:51.012597",
|
"creation": "2016-06-01 14:38:51.012597",
|
||||||
@@ -63,6 +64,7 @@
|
|||||||
"cost_center",
|
"cost_center",
|
||||||
"section_break_12",
|
"section_break_12",
|
||||||
"status",
|
"status",
|
||||||
|
"custom_remarks",
|
||||||
"remarks",
|
"remarks",
|
||||||
"column_break_16",
|
"column_break_16",
|
||||||
"letter_head",
|
"letter_head",
|
||||||
@@ -462,7 +464,8 @@
|
|||||||
"fieldname": "remarks",
|
"fieldname": "remarks",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Remarks",
|
"label": "Remarks",
|
||||||
"no_copy": 1
|
"no_copy": 1,
|
||||||
|
"read_only_depends_on": "eval:doc.custom_remarks == 0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_16",
|
"fieldname": "column_break_16",
|
||||||
@@ -573,10 +576,18 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "\nDraft\nSubmitted\nCancelled",
|
"options": "\nDraft\nSubmitted\nCancelled",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "custom_remarks",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Custom Remarks"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-12-08 13:02:30.016610",
|
"links": [],
|
||||||
|
"modified": "2020-09-02 13:39:43.383705",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
|
||||||
|
|
||||||
def set_remarks(self):
|
def set_remarks(self):
|
||||||
if self.remarks: return
|
if self.custom_remarks: return
|
||||||
|
|
||||||
if self.payment_type=="Internal Transfer":
|
if self.payment_type=="Internal Transfer":
|
||||||
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
|
||||||
@@ -897,7 +897,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
total_amount = ref_doc.get("grand_total")
|
total_amount = ref_doc.get("grand_total")
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
if reference_doctype == "Dunning":
|
elif reference_doctype == "Dunning":
|
||||||
total_amount = ref_doc.get("dunning_amount")
|
total_amount = ref_doc.get("dunning_amount")
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
outstanding_amount = ref_doc.get("dunning_amount")
|
outstanding_amount = ref_doc.get("dunning_amount")
|
||||||
@@ -1172,30 +1172,23 @@ def make_payment_order(source_name, target_doc=None):
|
|||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.payment_order_type = "Payment Entry"
|
target.payment_order_type = "Payment Entry"
|
||||||
|
target.append('references', dict(
|
||||||
|
reference_doctype="Payment Entry",
|
||||||
|
reference_name=source.name,
|
||||||
|
bank_account=source.party_bank_account,
|
||||||
|
amount=source.paid_amount,
|
||||||
|
account=source.paid_to,
|
||||||
|
supplier=source.party,
|
||||||
|
mode_of_payment=source.mode_of_payment,
|
||||||
|
))
|
||||||
|
|
||||||
def update_item(source_doc, target_doc, source_parent):
|
doclist = get_mapped_doc("Payment Entry", source_name, {
|
||||||
target_doc.bank_account = source_parent.party_bank_account
|
|
||||||
target_doc.amount = source_doc.allocated_amount
|
|
||||||
target_doc.account = source_parent.paid_to
|
|
||||||
target_doc.payment_entry = source_parent.name
|
|
||||||
target_doc.supplier = source_parent.party
|
|
||||||
target_doc.mode_of_payment = source_parent.mode_of_payment
|
|
||||||
|
|
||||||
|
|
||||||
doclist = get_mapped_doc("Payment Entry", source_name, {
|
|
||||||
"Payment Entry": {
|
"Payment Entry": {
|
||||||
"doctype": "Payment Order",
|
"doctype": "Payment Order",
|
||||||
"validation": {
|
|
||||||
"docstatus": ["=", 1]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Payment Entry Reference": {
|
|
||||||
"doctype": "Payment Order Reference",
|
|
||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 1]
|
"docstatus": ["=", 1]
|
||||||
},
|
},
|
||||||
"postprocess": update_item
|
}
|
||||||
},
|
|
||||||
|
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
frappe.listview_settings['Payment Entry'] = {
|
frappe.listview_settings['Payment Entry'] = {
|
||||||
|
|
||||||
onload: function(listview) {
|
onload: function(listview) {
|
||||||
listview.page.fields_dict.party_type.get_query = function() {
|
if (listview.page.fields_dict.party_type) {
|
||||||
return {
|
listview.page.fields_dict.party_type.get_query = function() {
|
||||||
"filters": {
|
return {
|
||||||
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
"filters": {
|
||||||
}
|
"name": ["in", Object.keys(frappe.boot.party_account_types)],
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -21,10 +21,15 @@ class PaymentOrder(Document):
|
|||||||
if cancel:
|
if cancel:
|
||||||
status = 'Initiated'
|
status = 'Initiated'
|
||||||
|
|
||||||
ref_field = "status" if self.payment_order_type == "Payment Request" else "payment_order_status"
|
if self.payment_order_type == "Payment Request":
|
||||||
|
ref_field = "status"
|
||||||
|
ref_doc_field = frappe.scrub(self.payment_order_type)
|
||||||
|
else:
|
||||||
|
ref_field = "payment_order_status"
|
||||||
|
ref_doc_field = "reference_name"
|
||||||
|
|
||||||
for d in self.references:
|
for d in self.references:
|
||||||
frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status)
|
frappe.db.set_value(self.payment_order_type, d.get(ref_doc_field), ref_field, status)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
|
|||||||
@@ -5,6 +5,45 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from frappe.utils import getdate
|
||||||
|
from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, make_payment_order
|
||||||
|
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||||
|
|
||||||
class TestPaymentOrder(unittest.TestCase):
|
class TestPaymentOrder(unittest.TestCase):
|
||||||
pass
|
def setUp(self):
|
||||||
|
create_bank_account()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for bt in frappe.get_all("Payment Order"):
|
||||||
|
doc = frappe.get_doc("Payment Order", bt.name)
|
||||||
|
doc.cancel()
|
||||||
|
doc.delete()
|
||||||
|
|
||||||
|
def test_payment_order_creation_against_payment_entry(self):
|
||||||
|
purchase_invoice = make_purchase_invoice()
|
||||||
|
payment_entry = get_payment_entry("Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC")
|
||||||
|
payment_entry.reference_no = "_Test_Payment_Order"
|
||||||
|
payment_entry.reference_date = getdate()
|
||||||
|
payment_entry.party_bank_account = "Checking Account - Citi Bank"
|
||||||
|
payment_entry.insert()
|
||||||
|
payment_entry.submit()
|
||||||
|
|
||||||
|
doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry")
|
||||||
|
reference_doc = doc.get("references")[0]
|
||||||
|
self.assertEquals(reference_doc.reference_name, payment_entry.name)
|
||||||
|
self.assertEquals(reference_doc.reference_doctype, "Payment Entry")
|
||||||
|
self.assertEquals(reference_doc.supplier, "_Test Supplier")
|
||||||
|
self.assertEquals(reference_doc.amount, 250)
|
||||||
|
|
||||||
|
def create_payment_order_against_payment_entry(ref_doc, order_type):
|
||||||
|
payment_order = frappe.get_doc(dict(
|
||||||
|
doctype="Payment Order",
|
||||||
|
company="_Test Company",
|
||||||
|
payment_order_type=order_type,
|
||||||
|
company_bank_account="Checking Account - Citi Bank"
|
||||||
|
))
|
||||||
|
doc = make_payment_order(ref_doc.name, payment_order)
|
||||||
|
doc.save()
|
||||||
|
doc.submit()
|
||||||
|
return doc
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2018-07-20 16:38:06.630813",
|
"creation": "2018-07-20 16:38:06.630813",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@@ -10,7 +11,6 @@
|
|||||||
"column_break_4",
|
"column_break_4",
|
||||||
"supplier",
|
"supplier",
|
||||||
"payment_request",
|
"payment_request",
|
||||||
"payment_entry",
|
|
||||||
"mode_of_payment",
|
"mode_of_payment",
|
||||||
"bank_account_details",
|
"bank_account_details",
|
||||||
"bank_account",
|
"bank_account",
|
||||||
@@ -103,17 +103,12 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "payment_entry",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Payment Entry",
|
|
||||||
"options": "Payment Entry",
|
|
||||||
"read_only": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-08 13:56:25.724557",
|
"links": [],
|
||||||
|
"modified": "2020-09-04 08:29:51.014390",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Order Reference",
|
"name": "Payment Order Reference",
|
||||||
|
|||||||
@@ -291,11 +291,11 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-08-21 16:15:49.089450",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Period Closing Voucher",
|
"name": "Period Closing Voucher",
|
||||||
"owner": "jai@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
|
|||||||
@@ -55,14 +55,48 @@ frappe.ui.form.on('POS Closing Entry', {
|
|||||||
},
|
},
|
||||||
callback: (r) => {
|
callback: (r) => {
|
||||||
let pos_docs = r.message;
|
let pos_docs = r.message;
|
||||||
set_form_data(pos_docs, frm)
|
set_form_data(pos_docs, frm);
|
||||||
refresh_fields(frm)
|
refresh_fields(frm);
|
||||||
set_html_data(frm)
|
set_html_data(frm);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
cur_frm.cscript.before_pos_transactions_remove = function(doc, cdt, cdn) {
|
||||||
|
const removed_row = locals[cdt][cdn];
|
||||||
|
|
||||||
|
if (!removed_row.pos_invoice) return;
|
||||||
|
|
||||||
|
frappe.db.get_doc("POS Invoice", removed_row.pos_invoice).then(doc => {
|
||||||
|
cur_frm.doc.grand_total -= flt(doc.grand_total);
|
||||||
|
cur_frm.doc.net_total -= flt(doc.net_total);
|
||||||
|
cur_frm.doc.total_quantity -= flt(doc.total_qty);
|
||||||
|
refresh_payments(doc, cur_frm, 1);
|
||||||
|
refresh_taxes(doc, cur_frm, 1);
|
||||||
|
refresh_fields(cur_frm);
|
||||||
|
set_html_data(cur_frm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.ui.form.on('POS Invoice Reference', {
|
||||||
|
pos_invoice(frm, cdt, cdn) {
|
||||||
|
const added_row = locals[cdt][cdn];
|
||||||
|
|
||||||
|
if (!added_row.pos_invoice) return;
|
||||||
|
|
||||||
|
frappe.db.get_doc("POS Invoice", added_row.pos_invoice).then(doc => {
|
||||||
|
frm.doc.grand_total += flt(doc.grand_total);
|
||||||
|
frm.doc.net_total += flt(doc.net_total);
|
||||||
|
frm.doc.total_quantity += flt(doc.total_qty);
|
||||||
|
refresh_payments(doc, frm);
|
||||||
|
refresh_taxes(doc, frm);
|
||||||
|
refresh_fields(frm);
|
||||||
|
set_html_data(frm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frappe.ui.form.on('POS Closing Entry Detail', {
|
frappe.ui.form.on('POS Closing Entry Detail', {
|
||||||
closing_amount: (frm, cdt, cdn) => {
|
closing_amount: (frm, cdt, cdn) => {
|
||||||
const row = locals[cdt][cdn];
|
const row = locals[cdt][cdn];
|
||||||
@@ -76,8 +110,8 @@ function set_form_data(data, frm) {
|
|||||||
frm.doc.grand_total += flt(d.grand_total);
|
frm.doc.grand_total += flt(d.grand_total);
|
||||||
frm.doc.net_total += flt(d.net_total);
|
frm.doc.net_total += flt(d.net_total);
|
||||||
frm.doc.total_quantity += flt(d.total_qty);
|
frm.doc.total_quantity += flt(d.total_qty);
|
||||||
add_to_payments(d, frm);
|
refresh_payments(d, frm);
|
||||||
add_to_taxes(d, frm);
|
refresh_taxes(d, frm);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,11 +124,12 @@ function add_to_pos_transaction(d, frm) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_to_payments(d, frm) {
|
function refresh_payments(d, frm, remove) {
|
||||||
d.payments.forEach(p => {
|
d.payments.forEach(p => {
|
||||||
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
||||||
if (payment) {
|
if (payment) {
|
||||||
payment.expected_amount += flt(p.amount);
|
if (!remove) payment.expected_amount += flt(p.amount);
|
||||||
|
else payment.expected_amount -= flt(p.amount);
|
||||||
} else {
|
} else {
|
||||||
frm.add_child("payment_reconciliation", {
|
frm.add_child("payment_reconciliation", {
|
||||||
mode_of_payment: p.mode_of_payment,
|
mode_of_payment: p.mode_of_payment,
|
||||||
@@ -105,11 +140,12 @@ function add_to_payments(d, frm) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function add_to_taxes(d, frm) {
|
function refresh_taxes(d, frm, remove) {
|
||||||
d.taxes.forEach(t => {
|
d.taxes.forEach(t => {
|
||||||
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
|
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
|
||||||
if (tax) {
|
if (tax) {
|
||||||
tax.amount += flt(t.tax_amount);
|
if (!remove) tax.amount += flt(t.tax_amount);
|
||||||
|
else tax.amount -= flt(t.tax_amount);
|
||||||
} else {
|
} else {
|
||||||
frm.add_child("taxes", {
|
frm.add_child("taxes", {
|
||||||
account_head: t.account_head,
|
account_head: t.account_head,
|
||||||
|
|||||||
@@ -279,7 +279,8 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Return (Credit Note)",
|
"label": "Is Return (Credit Note)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"set_only_once": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break1",
|
"fieldname": "column_break1",
|
||||||
@@ -1578,9 +1579,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-29 15:08:39.337385",
|
"modified": "2020-09-07 12:43:09.138720",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Invoice",
|
"name": "POS Invoice",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import frappe
|
|||||||
import unittest, copy, time
|
import unittest, copy, time
|
||||||
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||||
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
|
||||||
class TestPOSInvoice(unittest.TestCase):
|
class TestPOSInvoice(unittest.TestCase):
|
||||||
def test_timestamp_change(self):
|
def test_timestamp_change(self):
|
||||||
@@ -182,8 +183,9 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
def test_pos_returns_with_repayment(self):
|
def test_pos_returns_with_repayment(self):
|
||||||
pos = create_pos_invoice(qty = 10, do_not_save=True)
|
pos = create_pos_invoice(qty = 10, do_not_save=True)
|
||||||
|
|
||||||
|
pos.set('payments', [])
|
||||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
|
||||||
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
|
||||||
pos.insert()
|
pos.insert()
|
||||||
pos.submit()
|
pos.submit()
|
||||||
|
|
||||||
@@ -200,8 +202,9 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
|
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
|
||||||
cost_center = "Main - _TC", do_not_save=True)
|
cost_center = "Main - _TC", do_not_save=True)
|
||||||
|
|
||||||
|
pos.set('payments', [])
|
||||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
|
||||||
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60})
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
|
||||||
|
|
||||||
pos.insert()
|
pos.insert()
|
||||||
pos.submit()
|
pos.submit()
|
||||||
@@ -219,19 +222,29 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
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
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
|
se = make_serialized_item(company='_Test Company with perpetual inventory',
|
||||||
|
target_warehouse="Stores - TCP1", cost_center='Main - TCP1', expense_account='Cost of Goods Sold - TCP1')
|
||||||
|
|
||||||
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
|
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
|
||||||
|
|
||||||
pos = create_pos_invoice(item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
pos = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1',
|
||||||
|
account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1',
|
||||||
|
expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1',
|
||||||
|
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
||||||
|
|
||||||
pos.get("items")[0].serial_no = serial_nos[0]
|
pos.get("items")[0].serial_no = serial_nos[0]
|
||||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000})
|
||||||
|
|
||||||
pos.insert()
|
pos.insert()
|
||||||
pos.submit()
|
pos.submit()
|
||||||
|
|
||||||
pos2 = create_pos_invoice(item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
pos2 = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1',
|
||||||
|
account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1',
|
||||||
|
expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1',
|
||||||
|
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
||||||
|
|
||||||
pos2.get("items")[0].serial_no = serial_nos[0]
|
pos2.get("items")[0].serial_no = serial_nos[0]
|
||||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000})
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, pos2.insert)
|
self.assertRaises(frappe.ValidationError, pos2.insert)
|
||||||
|
|
||||||
@@ -274,6 +287,119 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
after_redeem_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program)
|
after_redeem_lp_details = get_loyalty_program_details_with_points(inv.customer, company=inv.company, loyalty_program=inv.loyalty_program)
|
||||||
self.assertEqual(after_redeem_lp_details.loyalty_points, 9)
|
self.assertEqual(after_redeem_lp_details.loyalty_points, 9)
|
||||||
|
|
||||||
|
def test_merging_into_sales_invoice_with_discount(self):
|
||||||
|
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||||
|
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||||
|
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
test_user, pos_profile = init_user_and_profile()
|
||||||
|
pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1)
|
||||||
|
pos_inv.append('payments', {
|
||||||
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||||
|
})
|
||||||
|
pos_inv.submit()
|
||||||
|
|
||||||
|
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
||||||
|
pos_inv2.append('payments', {
|
||||||
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
|
||||||
|
})
|
||||||
|
pos_inv2.submit()
|
||||||
|
|
||||||
|
merge_pos_invoices()
|
||||||
|
|
||||||
|
pos_inv.load_from_db()
|
||||||
|
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||||
|
self.assertEqual(rounded_total, 3500)
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
|
||||||
|
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||||
|
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||||
|
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
test_user, pos_profile = init_user_and_profile()
|
||||||
|
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||||
|
pos_inv.append('payments', {
|
||||||
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||||
|
})
|
||||||
|
pos_inv.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 14,
|
||||||
|
'included_in_print_rate': 1
|
||||||
|
})
|
||||||
|
pos_inv.submit()
|
||||||
|
|
||||||
|
pos_inv2 = create_pos_invoice(rate=300, qty=2, do_not_submit=1)
|
||||||
|
pos_inv2.additional_discount_percentage = 10
|
||||||
|
pos_inv2.append('payments', {
|
||||||
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 540
|
||||||
|
})
|
||||||
|
pos_inv2.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 14,
|
||||||
|
'included_in_print_rate': 1
|
||||||
|
})
|
||||||
|
pos_inv2.submit()
|
||||||
|
|
||||||
|
merge_pos_invoices()
|
||||||
|
|
||||||
|
pos_inv.load_from_db()
|
||||||
|
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||||
|
self.assertEqual(rounded_total, 840)
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
def test_merging_with_validate_selling_price(self):
|
||||||
|
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||||
|
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||||
|
|
||||||
|
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||||
|
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
|
||||||
|
|
||||||
|
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=1, basic_rate=300)
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
test_user, pos_profile = init_user_and_profile()
|
||||||
|
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||||
|
pos_inv.append('payments', {
|
||||||
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||||
|
})
|
||||||
|
pos_inv.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 14,
|
||||||
|
'included_in_print_rate': 1
|
||||||
|
})
|
||||||
|
self.assertRaises(frappe.ValidationError, pos_inv.submit)
|
||||||
|
|
||||||
|
pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
|
||||||
|
pos_inv2.append('payments', {
|
||||||
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
|
||||||
|
})
|
||||||
|
pos_inv2.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 14,
|
||||||
|
'included_in_print_rate': 1
|
||||||
|
})
|
||||||
|
pos_inv2.submit()
|
||||||
|
|
||||||
|
merge_pos_invoices()
|
||||||
|
|
||||||
|
pos_inv2.load_from_db()
|
||||||
|
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
||||||
|
self.assertEqual(rounded_total, 400)
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 0)
|
||||||
|
|
||||||
def create_pos_invoice(**args):
|
def create_pos_invoice(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
pos_profile = None
|
pos_profile = None
|
||||||
@@ -282,6 +408,7 @@ def create_pos_invoice(**args):
|
|||||||
pos_profile.save()
|
pos_profile.save()
|
||||||
|
|
||||||
pos_inv = frappe.new_doc("POS Invoice")
|
pos_inv = frappe.new_doc("POS Invoice")
|
||||||
|
pos_inv.update(args)
|
||||||
pos_inv.update_stock = 1
|
pos_inv.update_stock = 1
|
||||||
pos_inv.is_pos = 1
|
pos_inv.is_pos = 1
|
||||||
pos_inv.pos_profile = args.pos_profile or pos_profile.name
|
pos_inv.pos_profile = args.pos_profile or pos_profile.name
|
||||||
@@ -299,7 +426,7 @@ def create_pos_invoice(**args):
|
|||||||
pos_inv.return_against = args.return_against
|
pos_inv.return_against = args.return_against
|
||||||
pos_inv.currency=args.currency or "INR"
|
pos_inv.currency=args.currency or "INR"
|
||||||
pos_inv.conversion_rate = args.conversion_rate or 1
|
pos_inv.conversion_rate = args.conversion_rate or 1
|
||||||
pos_inv.account_for_change_amount = "Cash - _TC"
|
pos_inv.account_for_change_amount = args.account_for_change_amount or "Cash - _TC"
|
||||||
|
|
||||||
pos_inv.append("items", {
|
pos_inv.append("items", {
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
|
|||||||
@@ -24,11 +24,20 @@ class POSInvoiceMergeLog(Document):
|
|||||||
|
|
||||||
def validate_pos_invoice_status(self):
|
def validate_pos_invoice_status(self):
|
||||||
for d in self.pos_invoices:
|
for d in self.pos_invoices:
|
||||||
status, docstatus = frappe.db.get_value('POS Invoice', d.pos_invoice, ['status', 'docstatus'])
|
status, docstatus, is_return, return_against = frappe.db.get_value(
|
||||||
|
'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
|
||||||
|
|
||||||
if docstatus != 1:
|
if docstatus != 1:
|
||||||
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
|
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
|
||||||
if status in ['Consolidated']:
|
if status == "Consolidated":
|
||||||
frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
|
frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
|
||||||
|
if is_return and return_against not in [d.pos_invoice for d in self.pos_invoices] and status != "Consolidated":
|
||||||
|
# if return entry is not getting merged in the current pos closing and if it is not consolidated
|
||||||
|
frappe.throw(
|
||||||
|
_("Row #{}: Return Invoice {} cannot be made against unconsolidated invoice. \
|
||||||
|
You can add original invoice {} manually to proceed.")
|
||||||
|
.format(d.idx, frappe.bold(d.pos_invoice), frappe.bold(return_against))
|
||||||
|
)
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
|
||||||
@@ -36,12 +45,12 @@ class POSInvoiceMergeLog(Document):
|
|||||||
returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
|
returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
|
||||||
sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
|
sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
|
||||||
|
|
||||||
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
sales_invoice, credit_note = "", ""
|
||||||
|
if sales:
|
||||||
|
sales_invoice = self.process_merging_into_sales_invoice(sales)
|
||||||
|
|
||||||
if len(returns):
|
if returns:
|
||||||
credit_note = self.process_merging_into_credit_note(returns)
|
credit_note = self.process_merging_into_credit_note(returns)
|
||||||
else:
|
|
||||||
credit_note = ""
|
|
||||||
|
|
||||||
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
|
||||||
|
|
||||||
@@ -87,17 +96,28 @@ class POSInvoiceMergeLog(Document):
|
|||||||
loyalty_amount_sum += doc.loyalty_amount
|
loyalty_amount_sum += doc.loyalty_amount
|
||||||
|
|
||||||
for item in doc.get('items'):
|
for item in doc.get('items'):
|
||||||
items.append(item)
|
found = False
|
||||||
|
for i in items:
|
||||||
|
if (i.item_code == item.item_code and not i.serial_no and not i.batch_no and
|
||||||
|
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
|
||||||
|
items.append(item)
|
||||||
|
|
||||||
for tax in doc.get('taxes'):
|
for tax in doc.get('taxes'):
|
||||||
found = False
|
found = False
|
||||||
for t in taxes:
|
for t in taxes:
|
||||||
if t.account_head == tax.account_head and t.cost_center == tax.cost_center and t.rate == tax.rate:
|
if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
|
||||||
t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount)
|
t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount)
|
||||||
t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount)
|
t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount)
|
||||||
found = True
|
found = True
|
||||||
if not found:
|
if not found:
|
||||||
tax.charge_type = 'Actual'
|
tax.charge_type = 'Actual'
|
||||||
|
tax.included_in_print_rate = 0
|
||||||
|
tax.tax_amount = tax.tax_amount_after_discount_amount
|
||||||
|
tax.base_tax_amount = tax.base_tax_amount_after_discount_amount
|
||||||
taxes.append(tax)
|
taxes.append(tax)
|
||||||
|
|
||||||
for payment in doc.get('payments'):
|
for payment in doc.get('payments'):
|
||||||
@@ -118,6 +138,8 @@ class POSInvoiceMergeLog(Document):
|
|||||||
invoice.set('items', items)
|
invoice.set('items', items)
|
||||||
invoice.set('payments', payments)
|
invoice.set('payments', payments)
|
||||||
invoice.set('taxes', taxes)
|
invoice.set('taxes', taxes)
|
||||||
|
invoice.additional_discount_percentage = 0
|
||||||
|
invoice.discount_amount = 0.0
|
||||||
|
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,14 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint, get_link_to_form
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.controllers.status_updater import StatusUpdater
|
from erpnext.controllers.status_updater import StatusUpdater
|
||||||
|
|
||||||
class POSOpeningEntry(StatusUpdater):
|
class POSOpeningEntry(StatusUpdater):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_pos_profile_and_cashier()
|
self.validate_pos_profile_and_cashier()
|
||||||
|
self.validate_payment_method_account()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
def validate_pos_profile_and_cashier(self):
|
def validate_pos_profile_and_cashier(self):
|
||||||
@@ -21,5 +22,13 @@ class POSOpeningEntry(StatusUpdater):
|
|||||||
if not cint(frappe.db.get_value("User", self.user, "enabled")):
|
if not cint(frappe.db.get_value("User", self.user, "enabled")):
|
||||||
frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
|
frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user)))
|
||||||
|
|
||||||
|
def validate_payment_method_account(self):
|
||||||
|
for d in self.balance_details:
|
||||||
|
account = frappe.db.get_value("Mode of Payment Account",
|
||||||
|
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||||
|
if not account:
|
||||||
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
|
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
@@ -15,15 +15,6 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
|
|||||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||||
return erpnext.queries.warehouse(frm.doc);
|
return erpnext.queries.warehouse(frm.doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.call({
|
|
||||||
method: "erpnext.accounts.doctype.pos_profile.pos_profile.get_series",
|
|
||||||
callback: function(r) {
|
|
||||||
if(!r.exc) {
|
|
||||||
set_field_options("naming_series", r.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on('POS Profile', {
|
frappe.ui.form.on('POS Profile', {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"disabled",
|
"disabled",
|
||||||
"section_break_2",
|
"section_break_2",
|
||||||
"naming_series",
|
|
||||||
"customer",
|
"customer",
|
||||||
"company",
|
"company",
|
||||||
"country",
|
"country",
|
||||||
@@ -59,17 +58,6 @@
|
|||||||
"fieldname": "section_break_2",
|
"fieldname": "section_break_2",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "naming_series",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Series",
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "naming_series",
|
|
||||||
"oldfieldtype": "Select",
|
|
||||||
"options": "[Select]",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "customer",
|
"fieldname": "customer",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -302,10 +290,10 @@
|
|||||||
"fieldname": "warehouse",
|
"fieldname": "warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Warehouse",
|
"label": "Warehouse",
|
||||||
|
"mandatory_depends_on": "update_stock",
|
||||||
"oldfieldname": "warehouse",
|
"oldfieldname": "warehouse",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Warehouse",
|
"options": "Warehouse"
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -323,7 +311,7 @@
|
|||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-06-29 12:20:30.977272",
|
"modified": "2020-10-01 17:29:27.759088",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Profile",
|
"name": "POS Profile",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.utils import cint, now
|
from frappe.utils import cint, now, get_link_to_form
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ class POSProfile(Document):
|
|||||||
self.validate_default_profile()
|
self.validate_default_profile()
|
||||||
self.validate_all_link_fields()
|
self.validate_all_link_fields()
|
||||||
self.validate_duplicate_groups()
|
self.validate_duplicate_groups()
|
||||||
self.check_default_payment()
|
self.validate_payment_methods()
|
||||||
|
|
||||||
def validate_default_profile(self):
|
def validate_default_profile(self):
|
||||||
for row in self.applicable_for_users:
|
for row in self.applicable_for_users:
|
||||||
@@ -52,14 +52,23 @@ class POSProfile(Document):
|
|||||||
if len(customer_groups) != len(set(customer_groups)):
|
if len(customer_groups) != len(set(customer_groups)):
|
||||||
frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
|
frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group")
|
||||||
|
|
||||||
def check_default_payment(self):
|
def validate_payment_methods(self):
|
||||||
if self.payments:
|
if not self.payments:
|
||||||
default_mode_of_payment = [d.default for d in self.payments if d.default]
|
frappe.throw(_("Payment methods are mandatory. Please add at least one payment method."))
|
||||||
if not default_mode_of_payment:
|
|
||||||
frappe.throw(_("Set default mode of payment"))
|
|
||||||
|
|
||||||
if len(default_mode_of_payment) > 1:
|
default_mode_of_payment = [d.default for d in self.payments if d.default]
|
||||||
frappe.throw(_("Multiple default mode of payment is not allowed"))
|
if not default_mode_of_payment:
|
||||||
|
frappe.throw(_("Please select a default mode of payment"))
|
||||||
|
|
||||||
|
if len(default_mode_of_payment) > 1:
|
||||||
|
frappe.throw(_("You can only select one mode of payment as default"))
|
||||||
|
|
||||||
|
for d in self.payments:
|
||||||
|
account = frappe.db.get_value("Mode of Payment Account",
|
||||||
|
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||||
|
if not account:
|
||||||
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
|
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
self.set_defaults()
|
self.set_defaults()
|
||||||
@@ -100,10 +109,6 @@ def get_child_nodes(group_type, root):
|
|||||||
return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
|
return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
|
||||||
lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1)
|
lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1)
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def get_series():
|
|
||||||
return frappe.get_meta("POS Invoice").get_field("naming_series").options or "s"
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import unittest
|
|||||||
from erpnext.stock.get_item_details import get_pos_profile
|
from erpnext.stock.get_item_details import get_pos_profile
|
||||||
from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes
|
from erpnext.accounts.doctype.pos_profile.pos_profile import get_child_nodes
|
||||||
|
|
||||||
|
test_dependencies = ['Item']
|
||||||
|
|
||||||
class TestPOSProfile(unittest.TestCase):
|
class TestPOSProfile(unittest.TestCase):
|
||||||
def test_pos_profile(self):
|
def test_pos_profile(self):
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ frappe.ui.form.on('POS Settings', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get_invoice_fields: function(frm) {
|
get_invoice_fields: function(frm) {
|
||||||
frappe.model.with_doctype("Sales Invoice", () => {
|
frappe.model.with_doctype("POS Invoice", () => {
|
||||||
var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
|
var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
|
||||||
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
|
if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 ||
|
||||||
d.fieldtype === 'Table') {
|
['Table', 'Button'].includes(d.fieldtype)) {
|
||||||
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
|
return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname };
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@@ -25,7 +25,7 @@ frappe.ui.form.on('POS Settings', {
|
|||||||
frappe.ui.form.on("POS Field", {
|
frappe.ui.form.on("POS Field", {
|
||||||
fieldname: function(frm, doctype, name) {
|
fieldname: function(frm, doctype, name) {
|
||||||
var doc = frappe.get_doc(doctype, name);
|
var doc = frappe.get_doc(doctype, name);
|
||||||
var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) {
|
var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) {
|
||||||
return doc.fieldname == d.fieldname ? d : null;
|
return doc.fieldname == d.fieldname ? d : null;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@
|
|||||||
"section_break_13",
|
"section_break_13",
|
||||||
"threshold_percentage",
|
"threshold_percentage",
|
||||||
"priority",
|
"priority",
|
||||||
|
"condition",
|
||||||
"column_break_66",
|
"column_break_66",
|
||||||
"apply_multiple_pricing_rules",
|
"apply_multiple_pricing_rules",
|
||||||
"apply_discount_on_rate",
|
"apply_discount_on_rate",
|
||||||
@@ -563,7 +564,7 @@
|
|||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-12 20:15:32.279592",
|
"modified": "2020-08-26 12:24:44.740734",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# MIT License. See license.txt
|
|
||||||
|
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
@@ -145,7 +144,7 @@ class PricingRule(Document):
|
|||||||
|
|
||||||
def validate_condition(self):
|
def validate_condition(self):
|
||||||
if self.condition and ("=" in self.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", self.condition):
|
if self.condition and ("=" in self.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", self.condition):
|
||||||
frappe.throw(_("Invalid condition in Pricing Rule - {0}").format(pricing_rule.name), frappe.ValidationError)
|
frappe.throw(_("Invalid condition expression"))
|
||||||
|
|
||||||
#--------------------------------------------------------------------------------
|
#--------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -214,7 +213,7 @@ def get_serial_no_for_item(args):
|
|||||||
|
|
||||||
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
||||||
get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
|
get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
|
||||||
|
|
||||||
if isinstance(doc, string_types):
|
if isinstance(doc, string_types):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
@@ -243,7 +242,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
|
|
||||||
update_args_for_pricing_rule(args)
|
update_args_for_pricing_rule(args)
|
||||||
|
|
||||||
pricing_rules = (get_applied_pricing_rules(args)
|
pricing_rules = (get_applied_pricing_rules(args.get('pricing_rules'))
|
||||||
if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
|
if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
|
||||||
|
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
@@ -371,8 +370,9 @@ def set_discount_amount(rate, item_details):
|
|||||||
item_details.rate = rate
|
item_details.rate = rate
|
||||||
|
|
||||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
|
||||||
for d in json.loads(pricing_rules):
|
get_pricing_rule_items)
|
||||||
|
for d in get_applied_pricing_rules(pricing_rules):
|
||||||
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
if not d or not frappe.db.exists("Pricing Rule", d): continue
|
||||||
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
|
||||||
|
|
||||||
|
|||||||
@@ -430,6 +430,33 @@ class TestPricingRule(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(details)
|
self.assertTrue(details)
|
||||||
|
|
||||||
|
def test_pricing_rule_for_condition(self):
|
||||||
|
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
|
||||||
|
|
||||||
|
make_pricing_rule(selling=1, margin_type="Percentage", \
|
||||||
|
condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10)
|
||||||
|
|
||||||
|
# Incorrect Customer and Correct is_return value
|
||||||
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0)
|
||||||
|
si.items[0].price_list_rate = 1000
|
||||||
|
si.submit()
|
||||||
|
item = si.items[0]
|
||||||
|
self.assertEquals(item.rate, 100)
|
||||||
|
|
||||||
|
# Correct Customer and Incorrect is_return value
|
||||||
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1)
|
||||||
|
si.items[0].price_list_rate = 1000
|
||||||
|
si.submit()
|
||||||
|
item = si.items[0]
|
||||||
|
self.assertEquals(item.rate, 100)
|
||||||
|
|
||||||
|
# Correct Customer and correct is_return value
|
||||||
|
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0)
|
||||||
|
si.items[0].price_list_rate = 1000
|
||||||
|
si.submit()
|
||||||
|
item = si.items[0]
|
||||||
|
self.assertEquals(item.rate, 900)
|
||||||
|
|
||||||
def make_pricing_rule(**args):
|
def make_pricing_rule(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
@@ -448,7 +475,8 @@ def make_pricing_rule(**args):
|
|||||||
"discount_percentage": args.discount_percentage or 0.0,
|
"discount_percentage": args.discount_percentage or 0.0,
|
||||||
"rate": args.rate or 0.0,
|
"rate": args.rate or 0.0,
|
||||||
"margin_type": args.margin_type,
|
"margin_type": args.margin_type,
|
||||||
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0
|
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
|
||||||
|
"condition": args.condition or ''
|
||||||
})
|
})
|
||||||
|
|
||||||
apply_on = doc.apply_on.replace(' ', '_').lower()
|
apply_on = doc.apply_on.replace(' ', '_').lower()
|
||||||
|
|||||||
@@ -61,11 +61,12 @@ def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
|
|||||||
try:
|
try:
|
||||||
if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()):
|
if frappe.safe_eval(pricing_rule.condition, None, doc.as_dict()):
|
||||||
filtered_pricing_rules.append(pricing_rule)
|
filtered_pricing_rules.append(pricing_rule)
|
||||||
except Exception as e:
|
except:
|
||||||
frappe.msgprint(_("Pricing Rule - " + pricing_rule.name + " - <b>condition</b> field error:<br>" + \
|
pass
|
||||||
str(e).capitalize() + "<br><br>Ignoring Pricing Rule"), indicator="orange", title=_("Warning"))
|
|
||||||
else:
|
else:
|
||||||
filtered_pricing_rules.append(pricing_rule)
|
filtered_pricing_rules.append(pricing_rule)
|
||||||
|
else:
|
||||||
|
filtered_pricing_rules = pricing_rules
|
||||||
|
|
||||||
return filtered_pricing_rules
|
return filtered_pricing_rules
|
||||||
|
|
||||||
@@ -465,9 +466,14 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
|
|
||||||
def get_applied_pricing_rules(item_row):
|
def get_applied_pricing_rules(pricing_rules):
|
||||||
return (json.loads(item_row.get("pricing_rules"))
|
if pricing_rules:
|
||||||
if item_row.get("pricing_rules") else [])
|
if pricing_rules.startswith('['):
|
||||||
|
return json.loads(pricing_rules)
|
||||||
|
else:
|
||||||
|
return pricing_rules.split(',')
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
||||||
free_item = pricing_rule.free_item
|
free_item = pricing_rule.free_item
|
||||||
|
|||||||
@@ -10,13 +10,15 @@ frappe.ui.form.on('Process Deferred Accounting', {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
if (frm.doc.company) {
|
type: function(frm) {
|
||||||
|
if (frm.doc.company && frm.doc.type) {
|
||||||
frm.set_query("account", function() {
|
frm.set_query("account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'company': frm.doc.company,
|
'company': frm.doc.company,
|
||||||
'root_type': 'Liability',
|
'root_type': frm.doc.type === 'Income' ? 'Liability' : 'Asset',
|
||||||
'is_group': 0
|
'is_group': 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.type",
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
@@ -73,9 +74,10 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-02-06 18:18:09.852844",
|
"modified": "2020-09-03 18:07:02.463754",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Process Deferred Accounting",
|
"name": "Process Deferred Accounting",
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<h1 class="text-center" style="page-break-before:always">{{ filters.party[0] }}</h1>
|
||||||
|
<h3 class="text-center">{{ _("Statement of Accounts") }}</h3>
|
||||||
|
|
||||||
|
<h5 class="text-center">
|
||||||
|
{{ frappe.format(filters.from_date, 'Date')}}
|
||||||
|
{{ _("to") }}
|
||||||
|
{{ frappe.format(filters.to_date, 'Date')}}
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 12%">{{ _("Date") }}</th>
|
||||||
|
<th style="width: 15%">{{ _("Ref") }}</th>
|
||||||
|
<th style="width: 25%">{{ _("Party") }}</th>
|
||||||
|
<th style="width: 15%">{{ _("Debit") }}</th>
|
||||||
|
<th style="width: 15%">{{ _("Credit") }}</th>
|
||||||
|
<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for row in data %}
|
||||||
|
<tr>
|
||||||
|
{% if(row.posting_date) %}
|
||||||
|
<td>{{ frappe.format(row.posting_date, 'Date') }}</td>
|
||||||
|
<td>{{ row.voucher_type }}
|
||||||
|
<br>{{ row.voucher_no }}</td>
|
||||||
|
<td>
|
||||||
|
{% if not (filters.party or filters.account) %}
|
||||||
|
{{ row.party or row.account }}
|
||||||
|
<br>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ _("Against") }}: {{ row.against }}
|
||||||
|
<br>{{ _("Remarks") }}: {{ row.remarks }}
|
||||||
|
{% if row.bill_no %}
|
||||||
|
<br>{{ _("Supplier Invoice No") }}: {{ row.bill_no }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right">
|
||||||
|
{{ frappe.utils.fmt_money(row.debit, filters.presentation_currency) }}</td>
|
||||||
|
<td style="text-align: right">
|
||||||
|
{{ frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}</td>
|
||||||
|
{% else %}
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td><b>{{ frappe.format(row.account, {fieldtype: "Link"}) or " " }}</b></td>
|
||||||
|
<td style="text-align: right">
|
||||||
|
{{ row.account and frappe.utils.fmt_money(row.debit, filters.presentation_currency) }}
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right">
|
||||||
|
{{ row.account and frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
<td style="text-align: right">
|
||||||
|
{{ frappe.utils.fmt_money(row.balance, filters.presentation_currency) }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br><br>
|
||||||
|
{% if aging %}
|
||||||
|
<h3 class="text-center">{{ _("Ageing Report Based On ") }} {{ aging.ageing_based_on }}</h3>
|
||||||
|
<h5 class="text-center">
|
||||||
|
{{ _("Up to " ) }} {{ frappe.format(filters.to_date, 'Date')}}
|
||||||
|
</h5>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 12%">30 Days</th>
|
||||||
|
<th style="width: 15%">60 Days</th>
|
||||||
|
<th style="width: 25%">90 Days</th>
|
||||||
|
<th style="width: 15%">120 Days</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ aging.range1 }}</td>
|
||||||
|
<td>{{ aging.range2 }}</td>
|
||||||
|
<td>{{ aging.range3 }}</td>
|
||||||
|
<td>{{ aging.range4 }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
<p class="text-right text-muted">Printed On {{ frappe.format(frappe.utils.get_datetime(), 'Datetime') }}</p>
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Process Statement Of Accounts', {
|
||||||
|
view_properties: function(frm) {
|
||||||
|
frappe.route_options = {doc_type: 'Customer'};
|
||||||
|
frappe.set_route("Form", "Customize Form");
|
||||||
|
},
|
||||||
|
refresh: function(frm){
|
||||||
|
if(!frm.doc.__islocal) {
|
||||||
|
frm.add_custom_button('Send Emails',function(){
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
|
||||||
|
args: {
|
||||||
|
"document_name": frm.doc.name,
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(r && r.message) {
|
||||||
|
frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'});
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
frappe.msgprint('No Records for these settings.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
frm.add_custom_button('Download',function(){
|
||||||
|
var url = frappe.urllib.get_full_url(
|
||||||
|
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
|
||||||
|
+ 'document_name='+encodeURIComponent(frm.doc.name))
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'GET',
|
||||||
|
success: function(result) {
|
||||||
|
if(jQuery.isEmptyObject(result)){
|
||||||
|
frappe.msgprint('No Records for these settings.');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
window.location = url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onload: function(frm) {
|
||||||
|
frm.set_query('currency', function(){
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'enabled': 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(frm.doc.__islocal){
|
||||||
|
frm.set_value('from_date', frappe.datetime.add_months(frappe.datetime.get_today(), -1));
|
||||||
|
frm.set_value('to_date', frappe.datetime.get_today());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
customer_collection: function(frm){
|
||||||
|
frm.set_value('collection_name', '');
|
||||||
|
if(frm.doc.customer_collection){
|
||||||
|
frm.get_field('collection_name').set_label(frm.doc.customer_collection);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
frequency: function(frm){
|
||||||
|
if(frm.doc.frequency != ''){
|
||||||
|
frm.set_value('start_date', frappe.datetime.get_today());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
frm.set_value('start_date', '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetch_customers: function(frm){
|
||||||
|
if(frm.doc.collection_name){
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.fetch_customers",
|
||||||
|
args: {
|
||||||
|
'customer_collection': frm.doc.customer_collection,
|
||||||
|
'collection_name': frm.doc.collection_name,
|
||||||
|
'primary_mandatory': frm.doc.primary_mandatory
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(!r.exc) {
|
||||||
|
if(r.message.length){
|
||||||
|
frm.clear_table('customers');
|
||||||
|
for (const customer of r.message){
|
||||||
|
var row = frm.add_child('customers');
|
||||||
|
row.customer = customer.name;
|
||||||
|
row.primary_email = customer.primary_email;
|
||||||
|
row.billing_email = customer.billing_email;
|
||||||
|
}
|
||||||
|
frm.refresh_field('customers');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
frappe.msgprint('No Customers found with selected options.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
frappe.throw('Enter ' + frm.doc.customer_collection + ' name.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on('Process Statement Of Accounts Customer', {
|
||||||
|
customer: function(frm, cdt, cdn){
|
||||||
|
var row = locals[cdt][cdn];
|
||||||
|
if (!row.customer){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.get_customer_emails',
|
||||||
|
args: {
|
||||||
|
'customer_name': row.customer,
|
||||||
|
'primary_mandatory': frm.doc.primary_mandatory
|
||||||
|
},
|
||||||
|
callback: function(r){
|
||||||
|
if(!r.exe){
|
||||||
|
if(r.message.length){
|
||||||
|
frappe.model.set_value(cdt, cdn, "primary_email", r.message[0])
|
||||||
|
frappe.model.set_value(cdt, cdn, "billing_email", r.message[1])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_workflow": 1,
|
||||||
|
"autoname": "Prompt",
|
||||||
|
"creation": "2020-05-22 16:46:18.712954",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"section_break_11",
|
||||||
|
"from_date",
|
||||||
|
"company",
|
||||||
|
"account",
|
||||||
|
"group_by",
|
||||||
|
"cost_center",
|
||||||
|
"column_break_14",
|
||||||
|
"to_date",
|
||||||
|
"finance_book",
|
||||||
|
"currency",
|
||||||
|
"project",
|
||||||
|
"section_break_3",
|
||||||
|
"customer_collection",
|
||||||
|
"collection_name",
|
||||||
|
"fetch_customers",
|
||||||
|
"column_break_6",
|
||||||
|
"primary_mandatory",
|
||||||
|
"column_break_17",
|
||||||
|
"customers",
|
||||||
|
"preferences",
|
||||||
|
"orientation",
|
||||||
|
"section_break_14",
|
||||||
|
"include_ageing",
|
||||||
|
"ageing_based_on",
|
||||||
|
"section_break_1",
|
||||||
|
"enable_auto_email",
|
||||||
|
"section_break_18",
|
||||||
|
"frequency",
|
||||||
|
"filter_duration",
|
||||||
|
"column_break_21",
|
||||||
|
"start_date",
|
||||||
|
"section_break_33",
|
||||||
|
"subject",
|
||||||
|
"column_break_28",
|
||||||
|
"cc_to",
|
||||||
|
"section_break_30",
|
||||||
|
"body",
|
||||||
|
"help_text"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "frequency",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Frequency",
|
||||||
|
"options": "Weekly\nMonthly\nQuarterly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.enable_auto_email == 0;",
|
||||||
|
"fieldname": "from_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "From Date",
|
||||||
|
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.enable_auto_email == 0;",
|
||||||
|
"fieldname": "to_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "To Date",
|
||||||
|
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Table MultiSelect",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "PSOA Cost Center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Table MultiSelect",
|
||||||
|
"label": "Project",
|
||||||
|
"options": "PSOA Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_3",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Customers"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_6",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_11",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "General Ledger Filters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_14",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_17",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "customer_collection",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Select Customers By",
|
||||||
|
"options": "\nCustomer Group\nTerritory\nSales Partner\nSales Person"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: doc.customer_collection !== ''",
|
||||||
|
"fieldname": "collection_name",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"label": "Recipient",
|
||||||
|
"options": "customer_collection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_1",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Email Settings"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Account",
|
||||||
|
"options": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "finance_book",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Finance Book",
|
||||||
|
"options": "Finance Book"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "preferences",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Print Preferences"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "orientation",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Orientation",
|
||||||
|
"options": "Landscape\nPortrait"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Today",
|
||||||
|
"fieldname": "start_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Start Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Group by Voucher (Consolidated)",
|
||||||
|
"fieldname": "group_by",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Group By",
|
||||||
|
"options": "\nGroup by Voucher\nGroup by Voucher (Consolidated)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Currency",
|
||||||
|
"options": "Currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "include_ageing",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Include Ageing Summary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Due Date",
|
||||||
|
"depends_on": "eval:doc.include_ageing === 1",
|
||||||
|
"fieldname": "ageing_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Ageing Based On",
|
||||||
|
"options": "Due Date\nPosting Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "enable_auto_email",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Enable Auto Email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_14",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: doc.enable_auto_email ==1",
|
||||||
|
"fieldname": "section_break_18",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_21",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: doc.customer_collection !== ''",
|
||||||
|
"fieldname": "fetch_customers",
|
||||||
|
"fieldtype": "Button",
|
||||||
|
"label": "Fetch Customers",
|
||||||
|
"options": "fetch_customers",
|
||||||
|
"print_hide": 1,
|
||||||
|
"report_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "primary_mandatory",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Send To Primary Contact"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cc_to",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "CC To",
|
||||||
|
"options": "User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "filter_duration",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Filter Duration (Months)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "customers",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Customers",
|
||||||
|
"options": "Process Statement Of Accounts Customer",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_28",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_30",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_33",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "help_text",
|
||||||
|
"fieldtype": "HTML",
|
||||||
|
"label": "Help Text",
|
||||||
|
"options": "<br>\n<h4>Note</h4>\n<ul>\n<li>\nYou can use <a href=\"https://jinja.palletsprojects.com/en/2.11.x/\" target=\"_blank\">Jinja tags</a> in <b>Subject</b> and <b>Body</b> fields for dynamic values.\n</li><li>\n All fields in this doctype are available under the <b>doc</b> object and all fields for the customer to whom the mail will go to is available under the <b>customer</b> object.\n</li></ul>\n<h4> Examples</h4>\n<!-- {% raw %} -->\n<ul>\n <li><b>Subject</b>:<br><br><pre><code>Statement Of Accounts for {{ customer.name }}</code></pre><br></li>\n <li><b>Body</b>: <br><br>\n<pre><code>Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.</code> </pre></li>\n</ul>\n<!-- {% endraw %} -->"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "subject",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Subject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "body",
|
||||||
|
"fieldtype": "Text Editor",
|
||||||
|
"label": "Body"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-08-08 08:47:09.185728",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Process Statement Of Accounts",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,271 @@
|
|||||||
|
# -*- 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 frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from erpnext.accounts.report.general_ledger.general_ledger import execute as get_soa
|
||||||
|
from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute as get_ageing
|
||||||
|
from frappe.core.doctype.communication.email import make
|
||||||
|
|
||||||
|
from frappe.utils.print_format import report_to_pdf
|
||||||
|
from frappe.utils.pdf import get_pdf
|
||||||
|
from frappe.utils import today, add_days, add_months, getdate, format_date
|
||||||
|
from frappe.utils.jinja import validate_template
|
||||||
|
|
||||||
|
import copy
|
||||||
|
from datetime import timedelta
|
||||||
|
from frappe.www.printview import get_print_style
|
||||||
|
|
||||||
|
class ProcessStatementOfAccounts(Document):
|
||||||
|
def validate(self):
|
||||||
|
if not self.subject:
|
||||||
|
self.subject = 'Statement Of Accounts for {{ customer.name }}'
|
||||||
|
if not self.body:
|
||||||
|
self.body = 'Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.'
|
||||||
|
|
||||||
|
validate_template(self.subject)
|
||||||
|
validate_template(self.body)
|
||||||
|
|
||||||
|
if not self.customers:
|
||||||
|
frappe.throw(frappe._('Customers not selected.'))
|
||||||
|
|
||||||
|
if self.enable_auto_email:
|
||||||
|
self.to_date = self.start_date
|
||||||
|
self.from_date = add_months(self.to_date, -1 * self.filter_duration)
|
||||||
|
|
||||||
|
|
||||||
|
def get_report_pdf(doc, consolidated=True):
|
||||||
|
statement_dict = {}
|
||||||
|
aging = ''
|
||||||
|
base_template_path = "frappe/www/printview.html"
|
||||||
|
template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
|
||||||
|
|
||||||
|
for entry in doc.customers:
|
||||||
|
if doc.include_ageing:
|
||||||
|
ageing_filters = frappe._dict({
|
||||||
|
'company': doc.company,
|
||||||
|
'report_date': doc.to_date,
|
||||||
|
'ageing_based_on': doc.ageing_based_on,
|
||||||
|
'range1': 30,
|
||||||
|
'range2': 60,
|
||||||
|
'range3': 90,
|
||||||
|
'range4': 120,
|
||||||
|
'customer': entry.customer
|
||||||
|
})
|
||||||
|
col1, aging = get_ageing(ageing_filters)
|
||||||
|
aging[0]['ageing_based_on'] = doc.ageing_based_on
|
||||||
|
|
||||||
|
tax_id = frappe.get_doc('Customer', entry.customer).tax_id
|
||||||
|
|
||||||
|
filters= frappe._dict({
|
||||||
|
'from_date': doc.from_date,
|
||||||
|
'to_date': doc.to_date,
|
||||||
|
'company': doc.company,
|
||||||
|
'finance_book': doc.finance_book if doc.finance_book else None,
|
||||||
|
"account": doc.account if doc.account else None,
|
||||||
|
'party_type': 'Customer',
|
||||||
|
'party': [entry.customer],
|
||||||
|
'group_by': doc.group_by,
|
||||||
|
'currency': doc.currency,
|
||||||
|
'cost_center': [cc.cost_center_name for cc in doc.cost_center],
|
||||||
|
'project': [p.project_name for p in doc.project],
|
||||||
|
'show_opening_entries': 0,
|
||||||
|
'include_default_book_entries': 0,
|
||||||
|
'show_cancelled_entries': 1,
|
||||||
|
'tax_id': tax_id if tax_id else None
|
||||||
|
})
|
||||||
|
col, res = get_soa(filters)
|
||||||
|
|
||||||
|
for x in [0, -2, -1]:
|
||||||
|
res[x]['account'] = res[x]['account'].replace("'","")
|
||||||
|
|
||||||
|
if len(res) == 3:
|
||||||
|
continue
|
||||||
|
html = frappe.render_template(template_path, \
|
||||||
|
{"filters": filters, "data": res, "aging": aging[0] if doc.include_ageing else None})
|
||||||
|
html = frappe.render_template(base_template_path, {"body": html, \
|
||||||
|
"css": get_print_style(), "title": "Statement For " + entry.customer})
|
||||||
|
statement_dict[entry.customer] = html
|
||||||
|
if not bool(statement_dict):
|
||||||
|
return False
|
||||||
|
elif consolidated:
|
||||||
|
result = ''.join(list(statement_dict.values()))
|
||||||
|
return get_pdf(result, {'orientation': doc.orientation})
|
||||||
|
else:
|
||||||
|
for customer, statement_html in statement_dict.items():
|
||||||
|
statement_dict[customer]=get_pdf(statement_html, {'orientation': doc.orientation})
|
||||||
|
return statement_dict
|
||||||
|
|
||||||
|
def get_customers_based_on_territory_or_customer_group(customer_collection, collection_name):
|
||||||
|
fields_dict = {
|
||||||
|
'Customer Group': 'customer_group',
|
||||||
|
'Territory': 'territory',
|
||||||
|
}
|
||||||
|
collection = frappe.get_doc(customer_collection, collection_name)
|
||||||
|
selected = [customer.name for customer in frappe.get_list(customer_collection, filters=[
|
||||||
|
['lft', '>=', collection.lft],
|
||||||
|
['rgt', '<=', collection.rgt]
|
||||||
|
],
|
||||||
|
fields=['name'],
|
||||||
|
order_by='lft asc, rgt desc'
|
||||||
|
)]
|
||||||
|
return frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||||
|
filters=[[fields_dict[customer_collection], 'IN', selected]])
|
||||||
|
|
||||||
|
def get_customers_based_on_sales_person(sales_person):
|
||||||
|
lft, rgt = frappe.db.get_value("Sales Person",
|
||||||
|
sales_person, ["lft", "rgt"])
|
||||||
|
records = frappe.db.sql("""
|
||||||
|
select distinct parent, parenttype
|
||||||
|
from `tabSales Team` steam
|
||||||
|
where parenttype = 'Customer'
|
||||||
|
and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
|
||||||
|
""", (lft, rgt), as_dict=1)
|
||||||
|
sales_person_records = frappe._dict()
|
||||||
|
for d in records:
|
||||||
|
sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
||||||
|
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||||
|
filters=[['name', 'in', list(sales_person_records['Customer'])]])
|
||||||
|
return customers
|
||||||
|
|
||||||
|
def get_recipients_and_cc(customer, doc):
|
||||||
|
recipients = []
|
||||||
|
for clist in doc.customers:
|
||||||
|
if clist.customer == customer:
|
||||||
|
recipients.append(clist.billing_email)
|
||||||
|
if doc.primary_mandatory and clist.primary_email:
|
||||||
|
recipients.append(clist.primary_email)
|
||||||
|
cc = []
|
||||||
|
if doc.cc_to != '':
|
||||||
|
try:
|
||||||
|
cc=[frappe.get_value('User', doc.cc_to, 'email')]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return recipients, cc
|
||||||
|
|
||||||
|
def get_context(customer, doc):
|
||||||
|
template_doc = copy.deepcopy(doc)
|
||||||
|
del template_doc.customers
|
||||||
|
template_doc.from_date = format_date(template_doc.from_date)
|
||||||
|
template_doc.to_date = format_date(template_doc.to_date)
|
||||||
|
return {
|
||||||
|
'doc': template_doc,
|
||||||
|
'customer': frappe.get_doc('Customer', customer),
|
||||||
|
'frappe': frappe.utils
|
||||||
|
}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def fetch_customers(customer_collection, collection_name, primary_mandatory):
|
||||||
|
customer_list = []
|
||||||
|
customers = []
|
||||||
|
|
||||||
|
if customer_collection == 'Sales Person':
|
||||||
|
customers = get_customers_based_on_sales_person(collection_name)
|
||||||
|
if not bool(customers):
|
||||||
|
frappe.throw('No Customers found with selected options.')
|
||||||
|
else:
|
||||||
|
if customer_collection == 'Sales Partner':
|
||||||
|
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||||
|
filters=[['default_sales_partner', '=', collection_name]])
|
||||||
|
else:
|
||||||
|
customers = get_customers_based_on_territory_or_customer_group(customer_collection, collection_name)
|
||||||
|
|
||||||
|
for customer in customers:
|
||||||
|
primary_email = customer.get('email_id') or ''
|
||||||
|
billing_email = get_customer_emails(customer.name, 1, billing_and_primary=False)
|
||||||
|
|
||||||
|
if billing_email == '' or (primary_email == '' and int(primary_mandatory)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
customer_list.append({
|
||||||
|
'name': customer.name,
|
||||||
|
'primary_email': primary_email,
|
||||||
|
'billing_email': billing_email
|
||||||
|
})
|
||||||
|
return customer_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
|
||||||
|
billing_email = frappe.db.sql("""
|
||||||
|
SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent \
|
||||||
|
WHERE l.link_doctype='Customer' and l.link_name='""" + customer_name + """' and \
|
||||||
|
c.is_billing_contact=1 \
|
||||||
|
order by c.creation desc""")
|
||||||
|
|
||||||
|
if len(billing_email) == 0 or (billing_email[0][0] is None):
|
||||||
|
if billing_and_primary:
|
||||||
|
frappe.throw('No billing email found for customer: '+ customer_name)
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if billing_and_primary:
|
||||||
|
primary_email = frappe.get_value('Customer', customer_name, 'email_id')
|
||||||
|
if primary_email is None and int(primary_mandatory):
|
||||||
|
frappe.throw('No primary email found for customer: '+ customer_name)
|
||||||
|
return [primary_email or '', billing_email[0][0]]
|
||||||
|
else:
|
||||||
|
return billing_email[0][0] or ''
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def download_statements(document_name):
|
||||||
|
doc = frappe.get_doc('Process Statement Of Accounts', document_name)
|
||||||
|
report = get_report_pdf(doc)
|
||||||
|
if report:
|
||||||
|
frappe.local.response.filename = doc.name + '.pdf'
|
||||||
|
frappe.local.response.filecontent = report
|
||||||
|
frappe.local.response.type = "download"
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def send_emails(document_name, from_scheduler=False):
|
||||||
|
doc = frappe.get_doc('Process Statement Of Accounts', document_name)
|
||||||
|
report = get_report_pdf(doc, consolidated=False)
|
||||||
|
|
||||||
|
if report:
|
||||||
|
for customer, report_pdf in report.items():
|
||||||
|
attachments = [{
|
||||||
|
'fname': customer + '.pdf',
|
||||||
|
'fcontent': report_pdf
|
||||||
|
}]
|
||||||
|
|
||||||
|
recipients, cc = get_recipients_and_cc(customer, doc)
|
||||||
|
context = get_context(customer, doc)
|
||||||
|
subject = frappe.render_template(doc.subject, context)
|
||||||
|
message = frappe.render_template(doc.body, context)
|
||||||
|
|
||||||
|
frappe.enqueue(
|
||||||
|
queue='short',
|
||||||
|
method=frappe.sendmail,
|
||||||
|
recipients=recipients,
|
||||||
|
sender=frappe.session.user,
|
||||||
|
cc=cc,
|
||||||
|
subject=subject,
|
||||||
|
message=message,
|
||||||
|
now=True,
|
||||||
|
reference_doctype='Process Statement Of Accounts',
|
||||||
|
reference_name=document_name,
|
||||||
|
attachments=attachments
|
||||||
|
)
|
||||||
|
|
||||||
|
if doc.enable_auto_email and from_scheduler:
|
||||||
|
new_to_date = getdate(today())
|
||||||
|
if doc.frequency == 'Weekly':
|
||||||
|
new_to_date = add_days(new_to_date, 7)
|
||||||
|
else:
|
||||||
|
new_to_date = add_months(new_to_date, 1 if doc.frequency == 'Monthly' else 3)
|
||||||
|
new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
|
||||||
|
doc.add_comment('Comment', 'Emails sent on: ' + frappe.utils.format_datetime(frappe.utils.now()))
|
||||||
|
doc.db_set('to_date', new_to_date, commit=True)
|
||||||
|
doc.db_set('from_date', new_from_date, commit=True)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def send_auto_email():
|
||||||
|
selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1})
|
||||||
|
for entry in selected:
|
||||||
|
send_emails(entry.name, from_scheduler=True)
|
||||||
|
return True
|
||||||
@@ -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 TestProcessStatementOfAccounts(unittest.TestCase):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_workflow": 1,
|
||||||
|
"creation": "2020-08-03 16:35:21.852178",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"customer",
|
||||||
|
"billing_email",
|
||||||
|
"primary_email"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Customer",
|
||||||
|
"options": "Customer",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "primary_email",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Primary Contact Email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "billing_email",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Billing Email"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-08-03 22:55:38.875601",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Process Statement Of Accounts Customer",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# -*- 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 frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class ProcessStatementOfAccountsCustomer(Document):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2020-08-03 16:56:45.744905",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"cost_center_name"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center_name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-08-03 16:56:45.744905",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "PSOA Cost Center",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# -*- 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 frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class PSOACostCenter(Document):
|
||||||
|
pass
|
||||||
0
erpnext/accounts/doctype/psoa_project/__init__.py
Normal file
0
erpnext/accounts/doctype/psoa_project/__init__.py
Normal file
30
erpnext/accounts/doctype/psoa_project/psoa_project.json
Normal file
30
erpnext/accounts/doctype/psoa_project/psoa_project.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2020-08-03 16:52:14.731978",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"project_name"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "project_name",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Project",
|
||||||
|
"options": "Project"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-08-03 16:53:39.219736",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "PSOA Project",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
10
erpnext/accounts/doctype/psoa_project/psoa_project.py
Normal file
10
erpnext/accounts/doctype/psoa_project/psoa_project.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# -*- 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 frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class PSOAProject(Document):
|
||||||
|
pass
|
||||||
@@ -25,6 +25,12 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
this.frm.set_df_property("credit_to", "print_hide", 0);
|
this.frm.set_df_property("credit_to", "print_hide", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger supplier event on load if supplier is available
|
||||||
|
// The reason for this is PI can be created from PR or PO and supplier is pre populated
|
||||||
|
if (this.frm.doc.supplier && this.frm.doc.__islocal) {
|
||||||
|
this.frm.trigger('supplier');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(doc) {
|
refresh: function(doc) {
|
||||||
@@ -135,6 +141,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
unblock_invoice: function() {
|
unblock_invoice: function() {
|
||||||
|
|||||||
@@ -180,7 +180,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "naming_series",
|
"oldfieldname": "naming_series",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "ACC-PINV-.YYYY.-",
|
"options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"set_only_once": 1
|
"set_only_once": 1
|
||||||
@@ -1334,7 +1334,7 @@
|
|||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-08-03 12:46:01.411074",
|
"modified": "2020-08-03 23:20:04.466153",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.due_date:
|
if not self.due_date:
|
||||||
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company, self.bill_date)
|
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company, self.bill_date)
|
||||||
|
|
||||||
|
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||||
|
if tds_category and not for_validate:
|
||||||
|
self.apply_tds = 1
|
||||||
|
self.tax_withholding_category = tds_category
|
||||||
|
|
||||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
def check_conversion_rate(self):
|
def check_conversion_rate(self):
|
||||||
@@ -400,8 +405,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||||
|
|
||||||
def make_gl_entries(self, gl_entries=None):
|
def make_gl_entries(self, gl_entries=None):
|
||||||
if not self.grand_total:
|
|
||||||
return
|
|
||||||
if not gl_entries:
|
if not gl_entries:
|
||||||
gl_entries = self.get_gl_entries()
|
gl_entries = self.get_gl_entries()
|
||||||
|
|
||||||
@@ -708,7 +711,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount / self.conversion_rate)
|
item.item_tax_amount / self.conversion_rate)
|
||||||
}, item=item))
|
}, item=item))
|
||||||
else:
|
else:
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
|
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||||
|
asset_category=item.asset_category,company=self.company)
|
||||||
|
|
||||||
cwip_account_currency = get_account_currency(cwip_account)
|
cwip_account_currency = get_account_currency(cwip_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
|||||||
@@ -1002,7 +1002,8 @@ def make_purchase_invoice(**args):
|
|||||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||||
"project": args.project,
|
"project": args.project,
|
||||||
"rejected_warehouse": args.rejected_warehouse or "",
|
"rejected_warehouse": args.rejected_warehouse or "",
|
||||||
"rejected_serial_no": args.rejected_serial_no or ""
|
"rejected_serial_no": args.rejected_serial_no or "",
|
||||||
|
"asset_location": args.location or ""
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.get_taxes_and_charges:
|
if args.get_taxes_and_charges:
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-05-22 12:43:10",
|
"creation": "2013-05-22 12:43:10",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -82,6 +81,7 @@
|
|||||||
"item_tax_rate",
|
"item_tax_rate",
|
||||||
"bom",
|
"bom",
|
||||||
"include_exploded_items",
|
"include_exploded_items",
|
||||||
|
"purchase_invoice_item",
|
||||||
"col_break6",
|
"col_break6",
|
||||||
"purchase_order",
|
"purchase_order",
|
||||||
"po_detail",
|
"po_detail",
|
||||||
@@ -769,12 +769,21 @@
|
|||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "col_break7",
|
"fieldname": "col_break7",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:parent.update_stock == 1",
|
||||||
|
"fieldname": "purchase_invoice_item",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
|
"label": "Purchase Invoice Item",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"modified": "2020-08-20 11:48:01.398356",
|
||||||
"modified": "2020-04-22 10:37:35.103176",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
|||||||
@@ -210,7 +210,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-12 14:53:47.679439",
|
"modified": "2020-09-18 17:26:09.703215",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Taxes and Charges",
|
"name": "Purchase Taxes and Charges",
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Taxes and Charges Template",
|
"name": "Purchase Taxes and Charges Template",
|
||||||
"owner": "wasim@webnotestech.com",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"email": 1,
|
"email": 1,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_workflow": 1,
|
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-24 19:29:05",
|
"creation": "2013-05-24 19:29:05",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -20,6 +19,7 @@
|
|||||||
"is_return",
|
"is_return",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"company",
|
"company",
|
||||||
|
"company_tax_id",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
"posting_time",
|
"posting_time",
|
||||||
"set_posting_time",
|
"set_posting_time",
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "naming_series",
|
"oldfieldname": "naming_series",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "ACC-SINV-.YYYY.-",
|
"options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"set_only_once": 1
|
"set_only_once": 1
|
||||||
@@ -448,7 +448,7 @@
|
|||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "po_no",
|
"fieldname": "po_no",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Data",
|
||||||
"hide_days": 1,
|
"hide_days": 1,
|
||||||
"hide_seconds": 1,
|
"hide_seconds": 1,
|
||||||
"label": "Customer's Purchase Order",
|
"label": "Customer's Purchase Order",
|
||||||
@@ -1826,7 +1826,7 @@
|
|||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hide_days": 1,
|
"hide_days": 1,
|
||||||
"hide_seconds": 1,
|
"hide_seconds": 1,
|
||||||
"label": "Sales Team1",
|
"label": "Sales Contributions and Incentives",
|
||||||
"oldfieldname": "sales_team",
|
"oldfieldname": "sales_team",
|
||||||
"oldfieldtype": "Table",
|
"oldfieldtype": "Table",
|
||||||
"options": "Sales Team",
|
"options": "Sales Team",
|
||||||
@@ -1927,6 +1927,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:(doc.is_pos && doc.is_consolidated)",
|
||||||
"fieldname": "is_consolidated",
|
"fieldname": "is_consolidated",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Consolidated",
|
"label": "Is Consolidated",
|
||||||
@@ -1941,13 +1942,20 @@
|
|||||||
"hide_seconds": 1,
|
"hide_seconds": 1,
|
||||||
"label": "Is Internal Customer",
|
"label": "Is Internal Customer",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "company.tax_id",
|
||||||
|
"fieldname": "company_tax_id",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Company Tax ID",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 181,
|
"idx": 181,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-18 05:07:16.725974",
|
"modified": "2020-10-09 15:59:57.544736",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
|
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form
|
||||||
from frappe import _, msgprint, throw
|
from frappe import _, msgprint, throw
|
||||||
from erpnext.accounts.party import get_party_account, get_due_date
|
from erpnext.accounts.party import get_party_account, get_due_date
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
@@ -428,7 +428,7 @@ class SalesInvoice(SellingController):
|
|||||||
if pos.get('account_for_change_amount'):
|
if pos.get('account_for_change_amount'):
|
||||||
self.account_for_change_amount = pos.get('account_for_change_amount')
|
self.account_for_change_amount = pos.get('account_for_change_amount')
|
||||||
|
|
||||||
for fieldname in ('naming_series', 'currency', 'letter_head', 'tc_name',
|
for fieldname in ('currency', 'letter_head', 'tc_name',
|
||||||
'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
|
'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
|
||||||
'write_off_cost_center', 'apply_discount_on', 'cost_center'):
|
'write_off_cost_center', 'apply_discount_on', 'cost_center'):
|
||||||
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
||||||
@@ -1372,7 +1372,7 @@ def get_bank_cash_account(mode_of_payment, company):
|
|||||||
{"parent": mode_of_payment, "company": company}, "default_account")
|
{"parent": mode_of_payment, "company": company}, "default_account")
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
.format(mode_of_payment))
|
.format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account"))
|
||||||
return {
|
return {
|
||||||
"account": account
|
"account": account
|
||||||
}
|
}
|
||||||
@@ -1612,15 +1612,14 @@ def update_multi_mode_option(doc, pos_profile):
|
|||||||
payment.type = payment_mode.type
|
payment.type = payment_mode.type
|
||||||
|
|
||||||
doc.set('payments', [])
|
doc.set('payments', [])
|
||||||
if not pos_profile or not pos_profile.get('payments'):
|
|
||||||
for payment_mode in get_all_mode_of_payments(doc):
|
|
||||||
append_payment(payment_mode)
|
|
||||||
return
|
|
||||||
|
|
||||||
for pos_payment_method in pos_profile.get('payments'):
|
for pos_payment_method in pos_profile.get('payments'):
|
||||||
pos_payment_method = pos_payment_method.as_dict()
|
pos_payment_method = pos_payment_method.as_dict()
|
||||||
|
|
||||||
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
|
payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
|
||||||
|
if not payment_mode:
|
||||||
|
frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}")
|
||||||
|
.format(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)), title=_("Missing Account"))
|
||||||
|
|
||||||
payment_mode[0].default = pos_payment_method.default
|
payment_mode[0].default = pos_payment_method.default
|
||||||
append_payment(payment_mode[0])
|
append_payment(payment_mode[0])
|
||||||
|
|
||||||
|
|||||||
@@ -206,10 +206,19 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"rate": 14,
|
"rate": 14,
|
||||||
'included_in_print_rate': 1
|
'included_in_print_rate': 1
|
||||||
})
|
})
|
||||||
|
si.append("taxes", {
|
||||||
|
"charge_type": "On Item Quantity",
|
||||||
|
"account_head": "_Test Account Education Cess - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "CESS",
|
||||||
|
"rate": 5,
|
||||||
|
'included_in_print_rate': 1
|
||||||
|
})
|
||||||
si.insert()
|
si.insert()
|
||||||
|
|
||||||
# with inclusive tax
|
# with inclusive tax
|
||||||
self.assertEqual(si.net_total, 4385.96)
|
self.assertEqual(si.items[0].net_amount, 3947.368421052631)
|
||||||
|
self.assertEqual(si.net_total, 3947.37)
|
||||||
self.assertEqual(si.grand_total, 5000)
|
self.assertEqual(si.grand_total, 5000)
|
||||||
|
|
||||||
si.reload()
|
si.reload()
|
||||||
@@ -222,8 +231,8 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.save()
|
si.save()
|
||||||
|
|
||||||
# with inclusive tax and additional discount
|
# with inclusive tax and additional discount
|
||||||
self.assertEqual(si.net_total, 4285.96)
|
self.assertEqual(si.net_total, 3847.37)
|
||||||
self.assertEqual(si.grand_total, 4885.99)
|
self.assertEqual(si.grand_total, 4886)
|
||||||
|
|
||||||
si.reload()
|
si.reload()
|
||||||
|
|
||||||
@@ -235,7 +244,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.save()
|
si.save()
|
||||||
|
|
||||||
# with inclusive tax and additional discount
|
# with inclusive tax and additional discount
|
||||||
self.assertEqual(si.net_total, 4298.25)
|
self.assertEqual(si.net_total, 3859.65)
|
||||||
self.assertEqual(si.grand_total, 4900.00)
|
self.assertEqual(si.grand_total, 4900.00)
|
||||||
|
|
||||||
def test_sales_invoice_discount_amount(self):
|
def test_sales_invoice_discount_amount(self):
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-06-04 11:02:19",
|
"creation": "2013-06-04 11:02:19",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -87,6 +86,7 @@
|
|||||||
"edit_references",
|
"edit_references",
|
||||||
"sales_order",
|
"sales_order",
|
||||||
"so_detail",
|
"so_detail",
|
||||||
|
"sales_invoice_item",
|
||||||
"column_break_74",
|
"column_break_74",
|
||||||
"delivery_note",
|
"delivery_note",
|
||||||
"dn_detail",
|
"dn_detail",
|
||||||
@@ -790,12 +790,22 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project"
|
"options": "Project"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:parent.update_stock == 1",
|
||||||
|
"fieldname": "sales_invoice_item",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"ignore_user_permissions": 1,
|
||||||
|
"label": "Sales Invoice Item",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-18 12:24:41.749986",
|
"modified": "2020-08-20 11:24:41.749986",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Shipping Rule', {
|
frappe.ui.form.on('Shipping Rule', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
frm.set_query("cost_center", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
frm.set_query("account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frm.trigger('toggle_reqd');
|
frm.trigger('toggle_reqd');
|
||||||
},
|
},
|
||||||
calculate_based_on: function(frm) {
|
calculate_based_on: function(frm) {
|
||||||
|
|||||||
@@ -345,13 +345,14 @@ class Subscription(Document):
|
|||||||
invoice.set_taxes()
|
invoice.set_taxes()
|
||||||
|
|
||||||
# Due date
|
# Due date
|
||||||
invoice.append(
|
if self.days_until_due:
|
||||||
'payment_schedule',
|
invoice.append(
|
||||||
{
|
'payment_schedule',
|
||||||
'due_date': add_days(invoice.posting_date, cint(self.days_until_due)),
|
{
|
||||||
'invoice_portion': 100
|
'due_date': add_days(invoice.posting_date, cint(self.days_until_due)),
|
||||||
}
|
'invoice_portion': 100
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Discounts
|
# Discounts
|
||||||
if self.additional_discount_percentage:
|
if self.additional_discount_percentage:
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import unittest
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from erpnext.accounts.doctype.subscription.subscription import get_prorata_factor
|
from erpnext.accounts.doctype.subscription.subscription import get_prorata_factor
|
||||||
from frappe.utils.data import nowdate, add_days, add_to_date, add_months, date_diff, flt, get_date_str
|
from frappe.utils.data import (nowdate, add_days, add_to_date, add_months, date_diff, flt, get_date_str,
|
||||||
|
get_first_day, get_last_day)
|
||||||
|
|
||||||
def create_plan():
|
def create_plan():
|
||||||
if not frappe.db.exists('Subscription Plan', '_Test Plan Name'):
|
if not frappe.db.exists('Subscription Plan', '_Test Plan Name'):
|
||||||
@@ -68,14 +68,14 @@ class TestSubscription(unittest.TestCase):
|
|||||||
subscription.party_type = 'Customer'
|
subscription.party_type = 'Customer'
|
||||||
subscription.party = '_Test Customer'
|
subscription.party = '_Test Customer'
|
||||||
subscription.trial_period_start = nowdate()
|
subscription.trial_period_start = nowdate()
|
||||||
subscription.trial_period_end = add_days(nowdate(), 30)
|
subscription.trial_period_end = add_months(nowdate(), 1)
|
||||||
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
|
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
|
||||||
subscription.save()
|
subscription.save()
|
||||||
|
|
||||||
self.assertEqual(subscription.trial_period_start, nowdate())
|
self.assertEqual(subscription.trial_period_start, nowdate())
|
||||||
self.assertEqual(subscription.trial_period_end, add_days(nowdate(), 30))
|
self.assertEqual(subscription.trial_period_end, add_months(nowdate(), 1))
|
||||||
self.assertEqual(add_days(subscription.trial_period_end, 1), get_date_str(subscription.current_invoice_start))
|
self.assertEqual(add_days(subscription.trial_period_end, 1), get_date_str(subscription.current_invoice_start))
|
||||||
self.assertEqual(add_days(subscription.current_invoice_start, 30), get_date_str(subscription.current_invoice_end))
|
self.assertEqual(add_to_date(subscription.current_invoice_start, months=1, days=-1), get_date_str(subscription.current_invoice_end))
|
||||||
self.assertEqual(subscription.invoices, [])
|
self.assertEqual(subscription.invoices, [])
|
||||||
self.assertEqual(subscription.status, 'Trialling')
|
self.assertEqual(subscription.status, 'Trialling')
|
||||||
|
|
||||||
|
|||||||
@@ -1,134 +1,66 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
"allow_rename": 1,
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2018-11-22 23:38:39.668804",
|
"creation": "2018-11-22 23:38:39.668804",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "title",
|
"fieldname": "title",
|
||||||
"fieldtype": "Data",
|
"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": "Title",
|
"label": "Title",
|
||||||
"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": 1
|
"unique": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
"links": [],
|
||||||
"hide_toolbar": 0,
|
"modified": "2020-08-30 19:41:25.783852",
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2020-01-15 17:14:28.951793",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Tax Category",
|
"name": "Tax Category",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
"share": 1
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,8 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from erpnext.accounts.doctype.tax_rule.tax_rule import IncorrectCustomerGroup, IncorrectSupplierType, ConflictingTaxRule, get_tax_template
|
from erpnext.accounts.doctype.tax_rule.tax_rule import IncorrectCustomerGroup, IncorrectSupplierType, ConflictingTaxRule, get_tax_template
|
||||||
|
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
|
||||||
|
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
|
||||||
|
|
||||||
test_records = frappe.get_test_records('Tax Rule')
|
test_records = frappe.get_test_records('Tax Rule')
|
||||||
|
|
||||||
@@ -144,6 +146,23 @@ class TestTaxRule(unittest.TestCase):
|
|||||||
self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
|
self.assertEqual(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
|
||||||
"_Test Sales Taxes and Charges Template 1 - _TC")
|
"_Test Sales Taxes and Charges Template 1 - _TC")
|
||||||
|
|
||||||
|
def test_taxes_fetch_via_tax_rule(self):
|
||||||
|
make_tax_rule(customer= "_Test Customer", billing_city = "_Test City",
|
||||||
|
sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
|
||||||
|
|
||||||
|
# create opportunity for customer
|
||||||
|
opportunity = make_opportunity(with_items=1)
|
||||||
|
|
||||||
|
# make quotation from opportunity
|
||||||
|
quotation = make_quotation(opportunity.name)
|
||||||
|
quotation.save()
|
||||||
|
|
||||||
|
self.assertEqual(quotation.taxes_and_charges, "_Test Sales Taxes and Charges Template - _TC")
|
||||||
|
|
||||||
|
# Check if accounts heads and rate fetched are also fetched from tax template or not
|
||||||
|
self.assertTrue(len(quotation.taxes) > 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def make_tax_rule(**args):
|
def make_tax_rule(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai
|
|||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where company = %s and
|
where company = %s and
|
||||||
party in %s and fiscal_year=%s and credit > 0
|
party in %s and fiscal_year=%s and credit > 0
|
||||||
|
and is_opening = 'No'
|
||||||
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
||||||
|
|
||||||
vouchers = [d.voucher_no for d in entries]
|
vouchers = [d.voucher_no for d in entries]
|
||||||
@@ -192,6 +193,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No
|
|||||||
select distinct voucher_no
|
select distinct voucher_no
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
where party in %s and %s and debit > 0
|
where party in %s and %s and debit > 0
|
||||||
|
and is_opening = 'No'
|
||||||
""", (tuple(suppliers), condition)) or []
|
""", (tuple(suppliers), condition)) or []
|
||||||
|
|
||||||
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ def validate_accounting_period(gl_map):
|
|||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
if accounting_periods:
|
if accounting_periods:
|
||||||
frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
|
frappe.throw(_("You cannot create or cancel any accounting entries with in the closed Accounting Period {0}")
|
||||||
.format(accounting_periods[0].name), ClosedAccountingPeriod)
|
.format(frappe.bold(accounting_periods[0].name)), ClosedAccountingPeriod)
|
||||||
|
|
||||||
def process_gl_map(gl_map, merge_entries=True):
|
def process_gl_map(gl_map, merge_entries=True):
|
||||||
if merge_entries:
|
if merge_entries:
|
||||||
@@ -301,8 +301,9 @@ def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
|||||||
})
|
})
|
||||||
|
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
|
validate_accounting_period(gl_entries)
|
||||||
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
|
||||||
|
set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
entry['name'] = None
|
entry['name'] = None
|
||||||
@@ -319,7 +320,6 @@ def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
|||||||
|
|
||||||
entry['remarks'] = "On cancellation of " + entry['voucher_no']
|
entry['remarks'] = "On cancellation of " + entry['voucher_no']
|
||||||
entry['is_cancelled'] = 1
|
entry['is_cancelled'] = 1
|
||||||
entry['posting_date'] = today()
|
|
||||||
|
|
||||||
if entry['debit'] or entry['credit']:
|
if entry['debit'] or entry['credit']:
|
||||||
make_entry(entry, adv_adj, "Yes")
|
make_entry(entry, adv_adj, "Yes")
|
||||||
@@ -342,7 +342,7 @@ def set_as_cancel(voucher_type, voucher_no):
|
|||||||
"""
|
"""
|
||||||
Set is_cancelled=1 in all original gl entries for the voucher
|
Set is_cancelled=1 in all original gl entries for the voucher
|
||||||
"""
|
"""
|
||||||
frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
|
frappe.db.sql("""UPDATE `tabGL Entry` SET is_cancelled = 1,
|
||||||
modified=%s, modified_by=%s
|
modified=%s, modified_by=%s
|
||||||
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
|
where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
|
||||||
(now(), frappe.session.user, voucher_type, voucher_no))
|
(now(), frappe.session.user, voucher_type, voucher_no))
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
|
|||||||
check_plaid_status() {
|
check_plaid_status() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
|
frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => {
|
||||||
if (r && r.enabled == "1") {
|
if (r && r.enabled === "1") {
|
||||||
me.plaid_status = "active"
|
me.plaid_status = "active"
|
||||||
} else {
|
} else {
|
||||||
me.plaid_status = "inactive"
|
me.plaid_status = "inactive"
|
||||||
@@ -214,31 +214,35 @@ erpnext.accounts.bankTransactionSync = class bankTransactionSync {
|
|||||||
|
|
||||||
init_config() {
|
init_config() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration')
|
||||||
.then(result => {
|
.then(result => {
|
||||||
me.plaid_env = result.plaid_env;
|
me.plaid_env = result.plaid_env;
|
||||||
me.plaid_public_key = result.plaid_public_key;
|
me.client_name = result.client_name;
|
||||||
me.client_name = result.client_name;
|
me.link_token = result.link_token;
|
||||||
me.sync_transactions()
|
me.sync_transactions();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_transactions() {
|
sync_transactions() {
|
||||||
const me = this;
|
const me = this;
|
||||||
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => {
|
frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => {
|
||||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', {
|
||||||
bank: v['bank'],
|
bank: r.bank,
|
||||||
bank_account: me.parent.bank_account,
|
bank_account: me.parent.bank_account,
|
||||||
freeze: true
|
freeze: true
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized")
|
let result_title = (result && result.length > 0)
|
||||||
|
? __("{0} bank transaction(s) created", [result.length])
|
||||||
|
: __("This bank account is already synchronized");
|
||||||
|
|
||||||
let result_msg = `
|
let result_msg = `
|
||||||
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
||||||
<h5 class="text-muted">${result_title}</h5>
|
<h5 class="text-muted">${result_title}</h5>
|
||||||
</div>`
|
</div>`
|
||||||
|
|
||||||
this.parent.$main_section.append(result_msg)
|
this.parent.$main_section.append(result_msg)
|
||||||
frappe.show_alert({message:__("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator:'green'});
|
frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' });
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -384,7 +388,7 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
|||||||
})
|
})
|
||||||
|
|
||||||
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
|
frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments',
|
||||||
{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")}
|
{ bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") }
|
||||||
).then((result) => {
|
).then((result) => {
|
||||||
me.make_dialog(result)
|
me.make_dialog(result)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -611,7 +611,7 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None, futur
|
|||||||
cond = "posting_date <= '{0}'".format(posting_date)
|
cond = "posting_date <= '{0}'".format(posting_date)
|
||||||
|
|
||||||
if company:
|
if company:
|
||||||
cond += "and company = '{0}'".format(company)
|
cond += "and company = {0}".format(frappe.db.escape(company))
|
||||||
|
|
||||||
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
||||||
FROM `tabGL Entry`
|
FROM `tabGL Entry`
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
filters: {
|
filters: {
|
||||||
'company': company
|
'company': company
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -617,9 +617,19 @@ class ReceivablePayableReport(object):
|
|||||||
elif party_type_field=="supplier":
|
elif party_type_field=="supplier":
|
||||||
self.add_supplier_filters(conditions, values)
|
self.add_supplier_filters(conditions, values)
|
||||||
|
|
||||||
|
if self.filters.cost_center:
|
||||||
|
self.get_cost_center_conditions(conditions)
|
||||||
|
|
||||||
self.add_accounting_dimensions_filters(conditions, values)
|
self.add_accounting_dimensions_filters(conditions, values)
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
|
def get_cost_center_conditions(self, conditions):
|
||||||
|
lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
|
||||||
|
cost_center_list = [center.name for center in frappe.get_list("Cost Center", filters = {'lft': (">=", lft), 'rgt': ("<=", rgt)})]
|
||||||
|
|
||||||
|
cost_center_string = '", "'.join(cost_center_list)
|
||||||
|
conditions.append('cost_center in ("{0}")'.format(cost_center_string))
|
||||||
|
|
||||||
def get_order_by_condition(self):
|
def get_order_by_condition(self):
|
||||||
if self.filters.get('group_by_party'):
|
if self.filters.get('group_by_party'):
|
||||||
return "order by party, posting_date"
|
return "order by party, posting_date"
|
||||||
@@ -643,8 +653,10 @@ class ReceivablePayableReport(object):
|
|||||||
account_type = "Receivable" if self.party_type == "Customer" else "Payable"
|
account_type = "Receivable" if self.party_type == "Customer" else "Payable"
|
||||||
accounts = [d.name for d in frappe.get_all("Account",
|
accounts = [d.name for d in frappe.get_all("Account",
|
||||||
filters={"account_type": account_type, "company": self.filters.company})]
|
filters={"account_type": account_type, "company": self.filters.company})]
|
||||||
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
|
||||||
values += accounts
|
if accounts:
|
||||||
|
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
||||||
|
values += accounts
|
||||||
|
|
||||||
def add_customer_filters(self, conditions, values):
|
def add_customer_filters(self, conditions, values):
|
||||||
if self.filters.get("customer_group"):
|
if self.filters.get("customer_group"):
|
||||||
|
|||||||
@@ -71,7 +71,22 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
fieldtype: "Check",
|
fieldtype: "Check",
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
"formatter": function (value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
|
||||||
|
if (column.fieldname.includes('variance')) {
|
||||||
|
|
||||||
|
if (data[column.fieldname] < 0) {
|
||||||
|
value = "<span style='color:red'>" + value + "</span>";
|
||||||
|
}
|
||||||
|
else if (data[column.fieldname] > 0) {
|
||||||
|
value = "<span style='color:green'>" + value + "</span>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
erpnext.dimension_filters.forEach((dimension) => {
|
erpnext.dimension_filters.forEach((dimension) => {
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
|||||||
"""accumulate children's values in parent accounts"""
|
"""accumulate children's values in parent accounts"""
|
||||||
for d in reversed(accounts):
|
for d in reversed(accounts):
|
||||||
if d.parent_account:
|
if d.parent_account:
|
||||||
account = d.parent_account.split('-')[0].strip()
|
account = d.parent_account.split(' - ')[0].strip()
|
||||||
if not accounts_by_name.get(account):
|
if not accounts_by_name.get(account):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -378,7 +378,7 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
|||||||
if filters and filters.get('presentation_currency') != d.default_currency:
|
if filters and filters.get('presentation_currency') != d.default_currency:
|
||||||
currency_info['company'] = d.name
|
currency_info['company'] = d.name
|
||||||
currency_info['company_currency'] = d.default_currency
|
currency_info['company_currency'] = d.default_currency
|
||||||
convert_to_presentation_currency(gl_entries, currency_info)
|
convert_to_presentation_currency(gl_entries, currency_info, filters.get('company'))
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
key = entry.account_number or entry.account_name
|
key = entry.account_number or entry.account_name
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ class PartyLedgerSummaryReport(object):
|
|||||||
from `tabGL Entry` gle
|
from `tabGL Entry` gle
|
||||||
{join}
|
{join}
|
||||||
where
|
where
|
||||||
gle.docstatus < 2 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
|
gle.docstatus < 2 and gle.is_cancelled = 0 and gle.party_type=%(party_type)s and ifnull(gle.party, '') != ''
|
||||||
and gle.posting_date <= %(to_date)s {conditions}
|
and gle.posting_date <= %(to_date)s {conditions}
|
||||||
order by gle.posting_date
|
order by gle.posting_date
|
||||||
""".format(join=join, join_field=join_field, conditions=conditions), self.filters, as_dict=True)
|
""".format(join=join, join_field=join_field, conditions=conditions), self.filters, as_dict=True)
|
||||||
@@ -248,7 +248,7 @@ class PartyLedgerSummaryReport(object):
|
|||||||
from
|
from
|
||||||
`tabGL Entry`
|
`tabGL Entry`
|
||||||
where
|
where
|
||||||
docstatus < 2
|
docstatus < 2 and is_cancelled = 0
|
||||||
and (voucher_type, voucher_no) in (
|
and (voucher_type, voucher_no) in (
|
||||||
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
|
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
|
||||||
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
|
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import frappe, erpnext
|
|||||||
from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
|
from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr)
|
from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr, cint)
|
||||||
|
|
||||||
from six import itervalues
|
from six import itervalues
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children
|
||||||
@@ -46,7 +46,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
|
|||||||
start_date = year_start_date
|
start_date = year_start_date
|
||||||
months = get_months(year_start_date, year_end_date)
|
months = get_months(year_start_date, year_end_date)
|
||||||
|
|
||||||
for i in range(math.ceil(months / months_to_add)):
|
for i in range(cint(math.ceil(months / months_to_add))):
|
||||||
period = frappe._dict({
|
period = frappe._dict({
|
||||||
"from_date": start_date
|
"from_date": start_date
|
||||||
})
|
})
|
||||||
@@ -423,7 +423,7 @@ def set_gl_entries_by_account(
|
|||||||
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
|
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
|
||||||
|
|
||||||
if filters and filters.get('presentation_currency'):
|
if filters and filters.get('presentation_currency'):
|
||||||
convert_to_presentation_currency(gl_entries, get_currency(filters))
|
convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get('company'))
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
gl_entries_by_account.setdefault(entry.account, []).append(entry)
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
return frappe.db.get_link_options('Project', txt);
|
return frappe.db.get_link_options('Project', txt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "include_dimensions",
|
||||||
|
"label": __("Consider Accounting Dimensions"),
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "show_opening_entries",
|
"fieldname": "show_opening_entries",
|
||||||
"label": __("Show Opening Entries"),
|
"label": __("Show Opening Entries"),
|
||||||
|
|||||||
@@ -43,8 +43,11 @@ def execute(filters=None):
|
|||||||
|
|
||||||
|
|
||||||
def validate_filters(filters, account_details):
|
def validate_filters(filters, account_details):
|
||||||
if not filters.get('company'):
|
if not filters.get("company"):
|
||||||
frappe.throw(_('{0} is mandatory').format(_('Company')))
|
frappe.throw(_("{0} is mandatory").format(_("Company")))
|
||||||
|
|
||||||
|
if not filters.get("from_date") and not filters.get("to_date"):
|
||||||
|
frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
|
||||||
|
|
||||||
if filters.get("account") and not account_details.get(filters.account):
|
if filters.get("account") and not account_details.get(filters.account):
|
||||||
frappe.throw(_("Account {0} does not exists").format(filters.account))
|
frappe.throw(_("Account {0} does not exists").format(filters.account))
|
||||||
@@ -106,15 +109,20 @@ def set_account_currency(filters):
|
|||||||
return filters
|
return filters
|
||||||
|
|
||||||
def get_result(filters, account_details):
|
def get_result(filters, account_details):
|
||||||
gl_entries = get_gl_entries(filters)
|
accounting_dimensions = []
|
||||||
|
if filters.get("include_dimensions"):
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
data = get_data_with_opening_closing(filters, account_details, gl_entries)
|
gl_entries = get_gl_entries(filters, accounting_dimensions)
|
||||||
|
|
||||||
|
data = get_data_with_opening_closing(filters, account_details,
|
||||||
|
accounting_dimensions, gl_entries)
|
||||||
|
|
||||||
result = get_result_as_list(data, filters)
|
result = get_result_as_list(data, filters)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_gl_entries(filters):
|
def get_gl_entries(filters, accounting_dimensions):
|
||||||
currency_map = get_currency(filters)
|
currency_map = get_currency(filters)
|
||||||
select_fields = """, debit, credit, debit_in_account_currency,
|
select_fields = """, debit, credit, debit_in_account_currency,
|
||||||
credit_in_account_currency """
|
credit_in_account_currency """
|
||||||
@@ -128,6 +136,10 @@ def get_gl_entries(filters):
|
|||||||
filters['company_fb'] = frappe.db.get_value("Company",
|
filters['company_fb'] = frappe.db.get_value("Company",
|
||||||
filters.get("company"), 'default_finance_book')
|
filters.get("company"), 'default_finance_book')
|
||||||
|
|
||||||
|
dimension_fields = ""
|
||||||
|
if accounting_dimensions:
|
||||||
|
dimension_fields = ', '.join(accounting_dimensions) + ','
|
||||||
|
|
||||||
distributed_cost_center_query = ""
|
distributed_cost_center_query = ""
|
||||||
if filters and filters.get('cost_center'):
|
if filters and filters.get('cost_center'):
|
||||||
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
|
select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency,
|
||||||
@@ -141,7 +153,7 @@ def get_gl_entries(filters):
|
|||||||
party_type,
|
party_type,
|
||||||
party,
|
party,
|
||||||
voucher_type,
|
voucher_type,
|
||||||
voucher_no,
|
voucher_no, {dimension_fields}
|
||||||
cost_center, project,
|
cost_center, project,
|
||||||
against_voucher_type,
|
against_voucher_type,
|
||||||
against_voucher,
|
against_voucher,
|
||||||
@@ -160,13 +172,14 @@ def get_gl_entries(filters):
|
|||||||
{conditions}
|
{conditions}
|
||||||
AND posting_date <= %(to_date)s
|
AND posting_date <= %(to_date)s
|
||||||
AND cost_center = DCC_allocation.parent
|
AND cost_center = DCC_allocation.parent
|
||||||
""".format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
|
""".format(dimension_fields=dimension_fields,select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", ''))
|
||||||
|
|
||||||
gl_entries = frappe.db.sql(
|
gl_entries = frappe.db.sql(
|
||||||
"""
|
"""
|
||||||
select
|
select
|
||||||
name as gl_entry, posting_date, account, party_type, party,
|
name as gl_entry, posting_date, account, party_type, party,
|
||||||
voucher_type, voucher_no, cost_center, project,
|
voucher_type, voucher_no, {dimension_fields}
|
||||||
|
cost_center, project,
|
||||||
against_voucher_type, against_voucher, account_currency,
|
against_voucher_type, against_voucher, account_currency,
|
||||||
remarks, against, is_opening, creation {select_fields}
|
remarks, against, is_opening, creation {select_fields}
|
||||||
from `tabGL Entry`
|
from `tabGL Entry`
|
||||||
@@ -174,13 +187,13 @@ def get_gl_entries(filters):
|
|||||||
{distributed_cost_center_query}
|
{distributed_cost_center_query}
|
||||||
{order_by_statement}
|
{order_by_statement}
|
||||||
""".format(
|
""".format(
|
||||||
select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
|
dimension_fields=dimension_fields, select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query,
|
||||||
order_by_statement=order_by_statement
|
order_by_statement=order_by_statement
|
||||||
),
|
),
|
||||||
filters, as_dict=1)
|
filters, as_dict=1)
|
||||||
|
|
||||||
if filters.get('presentation_currency'):
|
if filters.get('presentation_currency'):
|
||||||
return convert_to_presentation_currency(gl_entries, currency_map)
|
return convert_to_presentation_currency(gl_entries, currency_map, filters.get('company'))
|
||||||
else:
|
else:
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
@@ -247,12 +260,12 @@ def get_conditions(filters):
|
|||||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||||
|
|
||||||
|
|
||||||
def get_data_with_opening_closing(filters, account_details, gl_entries):
|
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
gle_map = initialize_gle_map(gl_entries, filters)
|
gle_map = initialize_gle_map(gl_entries, filters)
|
||||||
|
|
||||||
totals, entries = get_accountwise_gle(filters, gl_entries, gle_map)
|
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map)
|
||||||
|
|
||||||
# Opening for filtered account
|
# Opening for filtered account
|
||||||
data.append(totals.opening)
|
data.append(totals.opening)
|
||||||
@@ -318,7 +331,7 @@ def initialize_gle_map(gl_entries, filters):
|
|||||||
return gle_map
|
return gle_map
|
||||||
|
|
||||||
|
|
||||||
def get_accountwise_gle(filters, gl_entries, gle_map):
|
def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
|
||||||
totals = get_totals_dict()
|
totals = get_totals_dict()
|
||||||
entries = []
|
entries = []
|
||||||
consolidated_gle = OrderedDict()
|
consolidated_gle = OrderedDict()
|
||||||
@@ -350,8 +363,11 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
|||||||
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
|
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
|
||||||
gle_map[gle.get(group_by)].entries.append(gle)
|
gle_map[gle.get(group_by)].entries.append(gle)
|
||||||
elif filters.get("group_by") == _('Group by Voucher (Consolidated)'):
|
elif filters.get("group_by") == _('Group by Voucher (Consolidated)'):
|
||||||
key = (gle.get("voucher_type"), gle.get("voucher_no"),
|
keylist = [gle.get("voucher_type"), gle.get("voucher_no"), gle.get("account")]
|
||||||
gle.get("account"), gle.get("cost_center"))
|
for dim in accounting_dimensions:
|
||||||
|
keylist.append(gle.get(dim))
|
||||||
|
keylist.append(gle.get("cost_center"))
|
||||||
|
key = tuple(keylist)
|
||||||
if key not in consolidated_gle:
|
if key not in consolidated_gle:
|
||||||
consolidated_gle.setdefault(key, gle)
|
consolidated_gle.setdefault(key, gle)
|
||||||
else:
|
else:
|
||||||
@@ -478,7 +494,19 @@ def get_columns(filters):
|
|||||||
"options": "Project",
|
"options": "Project",
|
||||||
"fieldname": "project",
|
"fieldname": "project",
|
||||||
"width": 100
|
"width": 100
|
||||||
},
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
if filters.get("include_dimensions"):
|
||||||
|
for dim in get_accounting_dimensions(as_list = False):
|
||||||
|
columns.append({
|
||||||
|
"label": _(dim.label),
|
||||||
|
"options": dim.label,
|
||||||
|
"fieldname": dim.fieldname,
|
||||||
|
"width": 100
|
||||||
|
})
|
||||||
|
|
||||||
|
columns.extend([
|
||||||
{
|
{
|
||||||
"label": _("Cost Center"),
|
"label": _("Cost Center"),
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 0,
|
"add_total_row": 1,
|
||||||
"apply_user_permissions": 1,
|
|
||||||
"creation": "2013-02-25 17:03:34",
|
"creation": "2013-02-25 17:03:34",
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
"idx": 3,
|
"idx": 3,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2017-02-24 20:12:22.464240",
|
"modified": "2020-08-13 11:26:39.112352",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Gross Profit",
|
"name": "Gross Profit",
|
||||||
|
|||||||
@@ -268,9 +268,9 @@ class GrossProfitGenerator(object):
|
|||||||
def get_last_purchase_rate(self, item_code, row):
|
def get_last_purchase_rate(self, item_code, row):
|
||||||
condition = ''
|
condition = ''
|
||||||
if row.project:
|
if row.project:
|
||||||
condition += " AND a.project='%s'" % (row.project)
|
condition += " AND a.project=%s" % (frappe.db.escape(row.project))
|
||||||
elif row.cost_center:
|
elif row.cost_center:
|
||||||
condition += " AND a.cost_center='%s'" % (row.cost_center)
|
condition += " AND a.cost_center=%s" % (frappe.db.escape(row.cost_center))
|
||||||
if self.filters.to_date:
|
if self.filters.to_date:
|
||||||
condition += " AND modified='%s'" % (self.filters.to_date)
|
condition += " AND modified='%s'" % (self.filters.to_date)
|
||||||
|
|
||||||
|
|||||||
0
erpnext/accounts/report/pos_register/__init__.py
Normal file
0
erpnext/accounts/report/pos_register/__init__.py
Normal file
76
erpnext/accounts/report/pos_register/pos_register.js
Normal file
76
erpnext/accounts/report/pos_register/pos_register.js
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["POS Register"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname":"company",
|
||||||
|
"label": __("Company"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Company",
|
||||||
|
"default": frappe.defaults.get_user_default("Company"),
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"from_date",
|
||||||
|
"label": __("From Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||||
|
"reqd": 1,
|
||||||
|
"width": "60px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"to_date",
|
||||||
|
"label": __("To Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"default": frappe.datetime.get_today(),
|
||||||
|
"reqd": 1,
|
||||||
|
"width": "60px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"pos_profile",
|
||||||
|
"label": __("POS Profile"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "POS Profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"cashier",
|
||||||
|
"label": __("Cashier"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"customer",
|
||||||
|
"label": __("Customer"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"mode_of_payment",
|
||||||
|
"label": __("Payment Method"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Mode of Payment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"group_by",
|
||||||
|
"label": __("Group by"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": ["", "POS Profile", "Cashier", "Payment Method", "Customer"],
|
||||||
|
"default": "POS Profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"is_return",
|
||||||
|
"label": __("Is Return"),
|
||||||
|
"fieldtype": "Check"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
|
value = default_formatter(value, row, column, data);
|
||||||
|
if (data && data.bold) {
|
||||||
|
value = value.bold();
|
||||||
|
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
30
erpnext/accounts/report/pos_register/pos_register.json
Normal file
30
erpnext/accounts/report/pos_register/pos_register.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 0,
|
||||||
|
"columns": [],
|
||||||
|
"creation": "2020-09-10 19:25:03.766871",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"json": "{}",
|
||||||
|
"modified": "2020-09-10 19:25:15.851331",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "POS Register",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "POS Invoice",
|
||||||
|
"report_name": "POS Register",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
222
erpnext/accounts/report/pos_register/pos_register.py
Normal file
222
erpnext/accounts/report/pos_register/pos_register.py
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _, _dict
|
||||||
|
from erpnext import get_company_currency, get_default_company
|
||||||
|
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
if not filters:
|
||||||
|
return [], []
|
||||||
|
|
||||||
|
validate_filters(filters)
|
||||||
|
|
||||||
|
columns = get_columns(filters)
|
||||||
|
|
||||||
|
group_by_field = get_group_by_field(filters.get("group_by"))
|
||||||
|
|
||||||
|
pos_entries = get_pos_entries(filters, group_by_field)
|
||||||
|
if group_by_field != "mode_of_payment":
|
||||||
|
concat_mode_of_payments(pos_entries)
|
||||||
|
|
||||||
|
# return only entries if group by is unselected
|
||||||
|
if not group_by_field:
|
||||||
|
return columns, pos_entries
|
||||||
|
|
||||||
|
# handle grouping
|
||||||
|
invoice_map, grouped_data = {}, []
|
||||||
|
for d in pos_entries:
|
||||||
|
invoice_map.setdefault(d[group_by_field], []).append(d)
|
||||||
|
|
||||||
|
for key in invoice_map:
|
||||||
|
invoices = invoice_map[key]
|
||||||
|
grouped_data += invoices
|
||||||
|
add_subtotal_row(grouped_data, invoices, group_by_field, key)
|
||||||
|
|
||||||
|
# move group by column to first position
|
||||||
|
column_index = next((index for (index, d) in enumerate(columns) if d["fieldname"] == group_by_field), None)
|
||||||
|
columns.insert(0, columns.pop(column_index))
|
||||||
|
|
||||||
|
return columns, grouped_data
|
||||||
|
|
||||||
|
def get_pos_entries(filters, group_by_field):
|
||||||
|
conditions = get_conditions(filters)
|
||||||
|
order_by = "p.posting_date"
|
||||||
|
select_mop_field, from_sales_invoice_payment, group_by_mop_condition = "", "", ""
|
||||||
|
if group_by_field == "mode_of_payment":
|
||||||
|
select_mop_field = ", sip.mode_of_payment"
|
||||||
|
from_sales_invoice_payment = ", `tabSales Invoice Payment` sip"
|
||||||
|
group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount, 0) != 0 AND"
|
||||||
|
order_by += ", sip.mode_of_payment"
|
||||||
|
|
||||||
|
elif group_by_field:
|
||||||
|
order_by += ", p.{}".format(group_by_field)
|
||||||
|
|
||||||
|
return frappe.db.sql(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
p.posting_date, p.name as pos_invoice, p.pos_profile,
|
||||||
|
p.owner, p.base_grand_total as grand_total, p.base_paid_amount as paid_amount,
|
||||||
|
p.customer, p.is_return {select_mop_field}
|
||||||
|
FROM
|
||||||
|
`tabPOS Invoice` p {from_sales_invoice_payment}
|
||||||
|
WHERE
|
||||||
|
{group_by_mop_condition}
|
||||||
|
{conditions}
|
||||||
|
ORDER BY
|
||||||
|
{order_by}
|
||||||
|
""".format(
|
||||||
|
select_mop_field=select_mop_field,
|
||||||
|
from_sales_invoice_payment=from_sales_invoice_payment,
|
||||||
|
group_by_mop_condition=group_by_mop_condition,
|
||||||
|
conditions=conditions,
|
||||||
|
order_by=order_by
|
||||||
|
), filters, as_dict=1)
|
||||||
|
|
||||||
|
def concat_mode_of_payments(pos_entries):
|
||||||
|
mode_of_payments = get_mode_of_payments(set([d.pos_invoice for d in pos_entries]))
|
||||||
|
for entry in pos_entries:
|
||||||
|
if mode_of_payments.get(entry.pos_invoice):
|
||||||
|
entry.mode_of_payment = ", ".join(mode_of_payments.get(entry.pos_invoice, []))
|
||||||
|
|
||||||
|
def add_subtotal_row(data, group_invoices, group_by_field, group_by_value):
|
||||||
|
grand_total = sum([d.grand_total for d in group_invoices])
|
||||||
|
paid_amount = sum([d.paid_amount for d in group_invoices])
|
||||||
|
data.append({
|
||||||
|
group_by_field: group_by_value,
|
||||||
|
"grand_total": grand_total,
|
||||||
|
"paid_amount": paid_amount,
|
||||||
|
"bold": 1
|
||||||
|
})
|
||||||
|
data.append({})
|
||||||
|
|
||||||
|
def validate_filters(filters):
|
||||||
|
if not filters.get("company"):
|
||||||
|
frappe.throw(_("{0} is mandatory").format(_("Company")))
|
||||||
|
|
||||||
|
if not filters.get("from_date") and not filters.get("to_date"):
|
||||||
|
frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
|
||||||
|
|
||||||
|
if filters.from_date > filters.to_date:
|
||||||
|
frappe.throw(_("From Date must be before To Date"))
|
||||||
|
|
||||||
|
if (filters.get("pos_profile") and filters.get("group_by") == _('POS Profile')):
|
||||||
|
frappe.throw(_("Can not filter based on POS Profile, if grouped by POS Profile"))
|
||||||
|
|
||||||
|
if (filters.get("customer") and filters.get("group_by") == _('Customer')):
|
||||||
|
frappe.throw(_("Can not filter based on Customer, if grouped by Customer"))
|
||||||
|
|
||||||
|
if (filters.get("owner") and filters.get("group_by") == _('Cashier')):
|
||||||
|
frappe.throw(_("Can not filter based on Cashier, if grouped by Cashier"))
|
||||||
|
|
||||||
|
if (filters.get("mode_of_payment") and filters.get("group_by") == _('Payment Method')):
|
||||||
|
frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
|
||||||
|
|
||||||
|
def get_conditions(filters):
|
||||||
|
conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format(
|
||||||
|
company=filters.get("company"),
|
||||||
|
from_date=filters.get("from_date"),
|
||||||
|
to_date=filters.get("to_date"))
|
||||||
|
|
||||||
|
if filters.get("pos_profile"):
|
||||||
|
conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile"))
|
||||||
|
|
||||||
|
if filters.get("owner"):
|
||||||
|
conditions += " AND owner = %(owner)s".format(owner=filters.get("owner"))
|
||||||
|
|
||||||
|
if filters.get("customer"):
|
||||||
|
conditions += " AND customer = %(customer)s".format(customer=filters.get("customer"))
|
||||||
|
|
||||||
|
if filters.get("is_return"):
|
||||||
|
conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return"))
|
||||||
|
|
||||||
|
if filters.get("mode_of_payment"):
|
||||||
|
conditions += """
|
||||||
|
AND EXISTS(
|
||||||
|
SELECT name FROM `tabSales Invoice Payment` sip
|
||||||
|
WHERE parent=p.name AND ifnull(sip.mode_of_payment, '') = %(mode_of_payment)s
|
||||||
|
)"""
|
||||||
|
|
||||||
|
return conditions
|
||||||
|
|
||||||
|
def get_group_by_field(group_by):
|
||||||
|
group_by_field = ""
|
||||||
|
|
||||||
|
if group_by == "POS Profile":
|
||||||
|
group_by_field = "pos_profile"
|
||||||
|
elif group_by == "Cashier":
|
||||||
|
group_by_field = "owner"
|
||||||
|
elif group_by == "Customer":
|
||||||
|
group_by_field = "customer"
|
||||||
|
elif group_by == "Payment Method":
|
||||||
|
group_by_field = "mode_of_payment"
|
||||||
|
|
||||||
|
return group_by_field
|
||||||
|
|
||||||
|
def get_columns(filters):
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
"label": _("Posting Date"),
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("POS Invoice"),
|
||||||
|
"fieldname": "pos_invoice",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "POS Invoice",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Customer"),
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Customer",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("POS Profile"),
|
||||||
|
"fieldname": "pos_profile",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "POS Profile",
|
||||||
|
"width": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Cashier"),
|
||||||
|
"fieldname": "owner",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "User",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Grand Total"),
|
||||||
|
"fieldname": "grand_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": "company:currency",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Paid Amount"),
|
||||||
|
"fieldname": "paid_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": "company:currency",
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Payment Method"),
|
||||||
|
"fieldname": "mode_of_payment",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Is Return"),
|
||||||
|
"fieldname": "is_return",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 80
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return columns
|
||||||
@@ -32,12 +32,12 @@ def execute(filters=None):
|
|||||||
|
|
||||||
chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
|
chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
|
||||||
|
|
||||||
default_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
|
currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||||
report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, default_currency)
|
report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency)
|
||||||
|
|
||||||
return columns, data, None, chart, report_summary
|
return columns, data, None, chart, report_summary
|
||||||
|
|
||||||
def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, default_currency, consolidated=False):
|
def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, consolidated=False):
|
||||||
net_income, net_expense, net_profit = 0.0, 0.0, 0.0
|
net_income, net_expense, net_profit = 0.0, 0.0, 0.0
|
||||||
|
|
||||||
for period in period_list:
|
for period in period_list:
|
||||||
@@ -64,19 +64,19 @@ def get_report_summary(period_list, periodicity, income, expense, net_profit_los
|
|||||||
"indicator": "Green" if net_profit > 0 else "Red",
|
"indicator": "Green" if net_profit > 0 else "Red",
|
||||||
"label": profit_label,
|
"label": profit_label,
|
||||||
"datatype": "Currency",
|
"datatype": "Currency",
|
||||||
"currency": net_profit_loss.get("currency") if net_profit_loss else default_currency
|
"currency": currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": net_income,
|
"value": net_income,
|
||||||
"label": income_label,
|
"label": income_label,
|
||||||
"datatype": "Currency",
|
"datatype": "Currency",
|
||||||
"currency": income[-1].get('currency') if income else default_currency
|
"currency": currency
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"value": net_expense,
|
"value": net_expense,
|
||||||
"label": expense_label,
|
"label": expense_label,
|
||||||
"datatype": "Currency",
|
"datatype": "Currency",
|
||||||
"currency": expense[-1].get('currency') if expense else default_currency
|
"currency": currency
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Finance Book",
|
"options": "Finance Book",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "presentation_currency",
|
||||||
|
"label": __("Currency"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": erpnext.get_presentation_currency_list()
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "with_period_closing_entry",
|
"fieldname": "with_period_closing_entry",
|
||||||
"label": __("Period Closing Entry"),
|
"label": __("Period Closing Entry"),
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ def get_data(filters):
|
|||||||
accounts = frappe.db.sql("""select name, account_number, parent_account, account_name, root_type, report_type, lft, rgt
|
accounts = frappe.db.sql("""select name, account_number, parent_account, account_name, root_type, report_type, lft, rgt
|
||||||
|
|
||||||
from `tabAccount` where company=%s order by lft""", filters.company, as_dict=True)
|
from `tabAccount` where company=%s order by lft""", filters.company, as_dict=True)
|
||||||
company_currency = erpnext.get_company_currency(filters.company)
|
company_currency = filters.presentation_currency or erpnext.get_company_currency(filters.company)
|
||||||
|
|
||||||
if not accounts:
|
if not accounts:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_dat
|
|||||||
from frappe.utils import cint, get_datetime_str, formatdate, flt
|
from frappe.utils import cint, get_datetime_str, formatdate, flt
|
||||||
|
|
||||||
__exchange_rates = {}
|
__exchange_rates = {}
|
||||||
P_OR_L_ACCOUNTS = list(
|
|
||||||
sum(frappe.get_list('Account', fields=['name'], or_filters=[{'root_type': 'Income'}, {'root_type': 'Expense'}], as_list=True), ())
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_currency(filters):
|
def get_currency(filters):
|
||||||
"""
|
"""
|
||||||
@@ -73,18 +69,7 @@ def get_rate_as_at(date, from_currency, to_currency):
|
|||||||
|
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
|
def convert_to_presentation_currency(gl_entries, currency_info, company):
|
||||||
def is_p_or_l_account(account_name):
|
|
||||||
"""
|
|
||||||
Check if the given `account name` is an `Account` with `root_type` of either 'Income'
|
|
||||||
or 'Expense'.
|
|
||||||
:param account_name:
|
|
||||||
:return: Boolean
|
|
||||||
"""
|
|
||||||
return account_name in P_OR_L_ACCOUNTS
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_presentation_currency(gl_entries, currency_info):
|
|
||||||
"""
|
"""
|
||||||
Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
|
Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
|
||||||
in `currency_info`.
|
in `currency_info`.
|
||||||
@@ -96,6 +81,9 @@ def convert_to_presentation_currency(gl_entries, currency_info):
|
|||||||
presentation_currency = currency_info['presentation_currency']
|
presentation_currency = currency_info['presentation_currency']
|
||||||
company_currency = currency_info['company_currency']
|
company_currency = currency_info['company_currency']
|
||||||
|
|
||||||
|
pl_accounts = [d.name for d in frappe.get_list('Account',
|
||||||
|
filters={'report_type': 'Profit and Loss', 'company': company})]
|
||||||
|
|
||||||
for entry in gl_entries:
|
for entry in gl_entries:
|
||||||
account = entry['account']
|
account = entry['account']
|
||||||
debit = flt(entry['debit'])
|
debit = flt(entry['debit'])
|
||||||
@@ -107,7 +95,7 @@ def convert_to_presentation_currency(gl_entries, currency_info):
|
|||||||
if account_currency != presentation_currency:
|
if account_currency != presentation_currency:
|
||||||
value = debit or credit
|
value = debit or credit
|
||||||
|
|
||||||
date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date']
|
date = entry['posting_date'] if account in pl_accounts else currency_info['report_date']
|
||||||
converted_value = convert(value, presentation_currency, company_currency, date)
|
converted_value = convert(value, presentation_currency, company_currency, date)
|
||||||
|
|
||||||
if entry.get('debit'):
|
if entry.get('debit'):
|
||||||
|
|||||||
@@ -683,6 +683,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
|
|||||||
where
|
where
|
||||||
party_type = %(party_type)s and party = %(party)s
|
party_type = %(party_type)s and party = %(party)s
|
||||||
and account = %(account)s and {dr_or_cr} > 0
|
and account = %(account)s and {dr_or_cr} > 0
|
||||||
|
and is_cancelled=0
|
||||||
{condition}
|
{condition}
|
||||||
and ((voucher_type = 'Journal Entry'
|
and ((voucher_type = 'Journal Entry'
|
||||||
and (against_voucher = '' or against_voucher is null))
|
and (against_voucher = '' or against_voucher is null))
|
||||||
@@ -705,6 +706,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters
|
|||||||
and account = %(account)s
|
and account = %(account)s
|
||||||
and {payment_dr_or_cr} > 0
|
and {payment_dr_or_cr} > 0
|
||||||
and against_voucher is not null and against_voucher != ''
|
and against_voucher is not null and against_voucher != ''
|
||||||
|
and is_cancelled=0
|
||||||
group by against_voucher_type, against_voucher
|
group by against_voucher_type, against_voucher
|
||||||
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
||||||
"party_type": party_type,
|
"party_type": party_type,
|
||||||
|
|||||||
@@ -252,13 +252,6 @@ frappe.ui.form.on('Asset', {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
available_for_use_date: function(frm) {
|
|
||||||
$.each(frm.doc.finance_books || [], function(i, d) {
|
|
||||||
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
|
|
||||||
});
|
|
||||||
refresh_field("finance_books");
|
|
||||||
},
|
|
||||||
|
|
||||||
is_existing_asset: function(frm) {
|
is_existing_asset: function(frm) {
|
||||||
frm.trigger("toggle_reference_doc");
|
frm.trigger("toggle_reference_doc");
|
||||||
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
|
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
|
||||||
@@ -438,6 +431,15 @@ frappe.ui.form.on('Asset Finance Book', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frappe.flags.dont_change_rate = false;
|
frappe.flags.dont_change_rate = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
depreciation_start_date: function(frm, cdt, cdn) {
|
||||||
|
const book = locals[cdt][cdn];
|
||||||
|
if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) {
|
||||||
|
frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`));
|
||||||
|
book.depreciation_start_date = "";
|
||||||
|
frm.refresh_field("finance_books");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
@@ -7,8 +8,9 @@
|
|||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
|
"is_existing_asset",
|
||||||
|
"section_break_2",
|
||||||
"naming_series",
|
"naming_series",
|
||||||
"asset_name",
|
|
||||||
"item_code",
|
"item_code",
|
||||||
"item_name",
|
"item_name",
|
||||||
"asset_category",
|
"asset_category",
|
||||||
@@ -17,29 +19,31 @@
|
|||||||
"supplier",
|
"supplier",
|
||||||
"customer",
|
"customer",
|
||||||
"image",
|
"image",
|
||||||
"purchase_invoice",
|
"journal_entry_for_scrap",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"company",
|
"company",
|
||||||
|
"asset_name",
|
||||||
"location",
|
"location",
|
||||||
"custodian",
|
"custodian",
|
||||||
"department",
|
"department",
|
||||||
"purchase_date",
|
|
||||||
"disposal_date",
|
"disposal_date",
|
||||||
"journal_entry_for_scrap",
|
|
||||||
"purchase_receipt",
|
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"section_break_5",
|
"purchase_details_section",
|
||||||
"gross_purchase_amount",
|
"purchase_receipt",
|
||||||
|
"purchase_invoice",
|
||||||
"available_for_use_date",
|
"available_for_use_date",
|
||||||
"column_break_18",
|
"column_break_23",
|
||||||
|
"gross_purchase_amount",
|
||||||
|
"purchase_date",
|
||||||
|
"section_break_23",
|
||||||
"calculate_depreciation",
|
"calculate_depreciation",
|
||||||
"allow_monthly_depreciation",
|
"allow_monthly_depreciation",
|
||||||
"is_existing_asset",
|
"column_break_33",
|
||||||
"opening_accumulated_depreciation",
|
"opening_accumulated_depreciation",
|
||||||
"number_of_depreciations_booked",
|
"number_of_depreciations_booked",
|
||||||
"section_break_23",
|
"section_break_36",
|
||||||
"finance_books",
|
"finance_books",
|
||||||
"section_break_33",
|
"section_break_33",
|
||||||
"depreciation_method",
|
"depreciation_method",
|
||||||
@@ -64,7 +68,6 @@
|
|||||||
"status",
|
"status",
|
||||||
"booked_fixed_asset",
|
"booked_fixed_asset",
|
||||||
"column_break_51",
|
"column_break_51",
|
||||||
|
|
||||||
"purchase_receipt_amount",
|
"purchase_receipt_amount",
|
||||||
"default_finance_book",
|
"default_finance_book",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
@@ -187,6 +190,8 @@
|
|||||||
"fieldname": "purchase_date",
|
"fieldname": "purchase_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Purchase Date",
|
"label": "Purchase Date",
|
||||||
|
"read_only": 1,
|
||||||
|
"read_only_depends_on": "eval:!doc.is_existing_asset",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -204,25 +209,20 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "section_break_5",
|
|
||||||
"fieldtype": "Section Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "gross_purchase_amount",
|
"fieldname": "gross_purchase_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Gross Purchase Amount",
|
"label": "Gross Purchase Amount",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1,
|
||||||
|
"read_only_depends_on": "eval:!doc.is_existing_asset",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "available_for_use_date",
|
"fieldname": "available_for_use_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Available-for-use Date"
|
"label": "Available-for-use Date",
|
||||||
},
|
"reqd": 1
|
||||||
{
|
|
||||||
"fieldname": "column_break_18",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -252,12 +252,14 @@
|
|||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "calculate_depreciation",
|
"collapsible": 1,
|
||||||
|
"collapsible_depends_on": "eval:doc.calculate_depreciation || doc.is_existing_asset",
|
||||||
"fieldname": "section_break_23",
|
"fieldname": "section_break_23",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Depreciation"
|
"label": "Depreciation"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 10,
|
||||||
"fieldname": "finance_books",
|
"fieldname": "finance_books",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Finance Books",
|
"label": "Finance Books",
|
||||||
@@ -305,8 +307,7 @@
|
|||||||
{
|
{
|
||||||
"depends_on": "calculate_depreciation",
|
"depends_on": "calculate_depreciation",
|
||||||
"fieldname": "section_break_14",
|
"fieldname": "section_break_14",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"label": "Depreciation Schedule"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "schedules",
|
"fieldname": "schedules",
|
||||||
@@ -456,12 +457,37 @@
|
|||||||
"fieldname": "allow_monthly_depreciation",
|
"fieldname": "allow_monthly_depreciation",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Allow Monthly Depreciation"
|
"label": "Allow Monthly Depreciation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_2",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"collapsible_depends_on": "is_existing_asset",
|
||||||
|
"fieldname": "purchase_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Purchase Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_23",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_33",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "calculate_depreciation",
|
||||||
|
"fieldname": "section_break_36",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 72,
|
"idx": 72,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-10-22 15:47:36.050828",
|
"links": [],
|
||||||
|
"modified": "2020-07-28 15:04:44.452224",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset",
|
"name": "Asset",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext, math, json
|
import frappe, erpnext, math, json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days
|
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days, get_last_day, get_datetime
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||||
from erpnext.assets.doctype.asset.depreciation \
|
from erpnext.assets.doctype.asset.depreciation \
|
||||||
@@ -83,6 +83,11 @@ class Asset(AccountsController):
|
|||||||
if not self.available_for_use_date:
|
if not self.available_for_use_date:
|
||||||
frappe.throw(_("Available for use date is required"))
|
frappe.throw(_("Available for use date is required"))
|
||||||
|
|
||||||
|
for d in self.finance_books:
|
||||||
|
if d.depreciation_start_date == self.available_for_use_date:
|
||||||
|
frappe.throw(_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(d.idx),
|
||||||
|
title=_("Incorrect Date"))
|
||||||
|
|
||||||
def set_missing_values(self):
|
def set_missing_values(self):
|
||||||
if not self.asset_category:
|
if not self.asset_category:
|
||||||
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
|
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
|
||||||
@@ -135,6 +140,10 @@ class Asset(AccountsController):
|
|||||||
def make_asset_movement(self):
|
def make_asset_movement(self):
|
||||||
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
|
||||||
reference_docname = self.purchase_receipt or self.purchase_invoice
|
reference_docname = self.purchase_receipt or self.purchase_invoice
|
||||||
|
transaction_date = getdate(self.purchase_date)
|
||||||
|
if reference_docname:
|
||||||
|
posting_date, posting_time = frappe.db.get_value(reference_doctype, reference_docname, ["posting_date", "posting_time"])
|
||||||
|
transaction_date = get_datetime("{} {}".format(posting_date, posting_time))
|
||||||
assets = [{
|
assets = [{
|
||||||
'asset': self.name,
|
'asset': self.name,
|
||||||
'asset_name': self.asset_name,
|
'asset_name': self.asset_name,
|
||||||
@@ -146,7 +155,7 @@ class Asset(AccountsController):
|
|||||||
'assets': assets,
|
'assets': assets,
|
||||||
'purpose': 'Receipt',
|
'purpose': 'Receipt',
|
||||||
'company': self.company,
|
'company': self.company,
|
||||||
'transaction_date': getdate(nowdate()),
|
'transaction_date': transaction_date,
|
||||||
'reference_doctype': reference_doctype,
|
'reference_doctype': reference_doctype,
|
||||||
'reference_name': reference_docname
|
'reference_name': reference_docname
|
||||||
}).insert()
|
}).insert()
|
||||||
@@ -294,7 +303,7 @@ class Asset(AccountsController):
|
|||||||
if not row.depreciation_start_date:
|
if not row.depreciation_start_date:
|
||||||
if not self.available_for_use_date:
|
if not self.available_for_use_date:
|
||||||
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
|
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
|
||||||
row.depreciation_start_date = self.available_for_use_date
|
row.depreciation_start_date = get_last_day(self.available_for_use_date)
|
||||||
|
|
||||||
if not self.is_existing_asset:
|
if not self.is_existing_asset:
|
||||||
self.opening_accumulated_depreciation = 0
|
self.opening_accumulated_depreciation = 0
|
||||||
@@ -457,29 +466,37 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
def validate_make_gl_entry(self):
|
def validate_make_gl_entry(self):
|
||||||
purchase_document = self.get_purchase_document()
|
purchase_document = self.get_purchase_document()
|
||||||
asset_bought_with_invoice = purchase_document == self.purchase_invoice
|
if not purchase_document:
|
||||||
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
|
||||||
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
|
||||||
# check if expense already has been booked in case of cwip was enabled after purchasing asset
|
|
||||||
expense_booked = False
|
|
||||||
cwip_booked = False
|
|
||||||
|
|
||||||
if asset_bought_with_invoice:
|
|
||||||
expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
|
||||||
(purchase_document, fixed_asset_account), as_dict=1)
|
|
||||||
else:
|
|
||||||
cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
|
||||||
(purchase_document, cwip_account), as_dict=1)
|
|
||||||
|
|
||||||
if cwip_enabled and (expense_booked or not cwip_booked):
|
|
||||||
# if expense has already booked from invoice or cwip is booked from receipt
|
|
||||||
return False
|
return False
|
||||||
elif not cwip_enabled and (not expense_booked or cwip_booked):
|
|
||||||
# if cwip is disabled but expense hasn't been booked yet
|
asset_bought_with_invoice = (purchase_document == self.purchase_invoice)
|
||||||
return True
|
fixed_asset_account = self.get_fixed_asset_account()
|
||||||
elif cwip_enabled:
|
|
||||||
# default condition
|
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
||||||
return True
|
cwip_account = self.get_cwip_account(cwip_enabled=cwip_enabled)
|
||||||
|
|
||||||
|
query = """SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s"""
|
||||||
|
if asset_bought_with_invoice:
|
||||||
|
# with invoice purchase either expense or cwip has been booked
|
||||||
|
expense_booked = frappe.db.sql(query, (purchase_document, fixed_asset_account), as_dict=1)
|
||||||
|
if expense_booked:
|
||||||
|
# if expense is already booked from invoice then do not make gl entries regardless of cwip enabled/disabled
|
||||||
|
return False
|
||||||
|
|
||||||
|
cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
|
||||||
|
if cwip_booked:
|
||||||
|
# if cwip is booked from invoice then make gl entries regardless of cwip enabled/disabled
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# with receipt purchase either cwip has been booked or no entries have been made
|
||||||
|
if not cwip_account:
|
||||||
|
# if cwip account isn't available do not make gl entries
|
||||||
|
return False
|
||||||
|
|
||||||
|
cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
|
||||||
|
# if cwip is not booked from receipt then do not make gl entries
|
||||||
|
# if cwip is booked from receipt then make gl entries
|
||||||
|
return cwip_booked
|
||||||
|
|
||||||
def get_purchase_document(self):
|
def get_purchase_document(self):
|
||||||
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
|
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
|
||||||
@@ -487,20 +504,25 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
return purchase_document
|
return purchase_document
|
||||||
|
|
||||||
def get_asset_accounts(self):
|
def get_fixed_asset_account(self):
|
||||||
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company)
|
||||||
asset_category = self.asset_category, company = self.company)
|
|
||||||
|
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
def get_cwip_account(self, cwip_enabled=False):
|
||||||
self.name, self.asset_category, self.company)
|
cwip_account = None
|
||||||
|
try:
|
||||||
|
cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
|
||||||
|
except:
|
||||||
|
# if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
|
||||||
|
if cwip_enabled:
|
||||||
|
raise
|
||||||
|
|
||||||
return fixed_asset_account, cwip_account
|
return cwip_account
|
||||||
|
|
||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
purchase_document = self.get_purchase_document()
|
purchase_document = self.get_purchase_document()
|
||||||
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
|
||||||
|
|
||||||
if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
||||||
|
|
||||||
@@ -552,14 +574,18 @@ class Asset(AccountsController):
|
|||||||
return 100 * (1 - flt(depreciation_rate, float_precision))
|
return 100 * (1 - flt(depreciation_rate, float_precision))
|
||||||
|
|
||||||
def update_maintenance_status():
|
def update_maintenance_status():
|
||||||
assets = frappe.get_all('Asset', filters = {'docstatus': 1, 'maintenance_required': 1})
|
assets = frappe.get_all(
|
||||||
|
"Asset", filters={"docstatus": 1, "maintenance_required": 1}
|
||||||
|
)
|
||||||
|
|
||||||
for asset in assets:
|
for asset in assets:
|
||||||
asset = frappe.get_doc("Asset", asset.name)
|
asset = frappe.get_doc("Asset", asset.name)
|
||||||
if frappe.db.exists('Asset Maintenance Task', {'parent': asset.name, 'next_due_date': today()}):
|
if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}):
|
||||||
asset.set_status('In Maintenance')
|
asset.set_status("Out of Order")
|
||||||
if frappe.db.exists('Asset Repair', {'asset_name': asset.name, 'repair_status': 'Pending'}):
|
elif frappe.db.exists("Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}):
|
||||||
asset.set_status('Out of Order')
|
asset.set_status("In Maintenance")
|
||||||
|
else:
|
||||||
|
asset.set_status()
|
||||||
|
|
||||||
def make_post_gl_entry():
|
def make_post_gl_entry():
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user