Merge branch 'develop' into refactor-addiional-salary
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index 8566ead..0d6aca6 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -8,7 +8,7 @@
{
"hidden": 0,
"label": "General Ledger",
- "links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]"
+ "links": "[\n {\n \"description\": \"Accounting journal entries.\",\n \"label\": \"Journal Entry\",\n \"name\": \"Journal Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Make journal entries from a template.\",\n \"label\": \"Journal Entry Template\",\n \"name\": \"Journal Entry Template\",\n \"type\": \"doctype\"\n },\n \n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"General Ledger\",\n \"name\": \"General Ledger\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Customer Ledger Summary\",\n \"name\": \"Customer Ledger Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Supplier Ledger Summary\",\n \"name\": \"Supplier Ledger Summary\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@@ -47,8 +47,8 @@
},
{
"hidden": 0,
- "label": "Banking and Payments",
- "links": "[\n {\n \"description\": \"Match non-linked Invoices and Payments.\",\n \"label\": \"Match Payments with Invoices\",\n \"name\": \"Payment Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update bank payment dates with journals.\",\n \"label\": \"Update Bank Transaction Dates\",\n \"name\": \"Bank Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Invoice Discounting\",\n \"name\": \"Invoice Discounting\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Clearance Summary\",\n \"name\": \"Bank Clearance Summary\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Guarantee\",\n \"name\": \"Bank Guarantee\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup cheque dimensions for printing\",\n \"label\": \"Cheque Print Template\",\n \"name\": \"Cheque Print Template\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"description\": \"Match non-linked Invoices and Payments.\",\n \"label\": \"Match Payments with Invoices\",\n \"name\": \"Payment Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update bank payment dates with journals.\",\n \"label\": \"Update Bank Clearance Dates\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Invoice Discounting\",\n \"name\": \"Invoice Discounting\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Clearance Summary\",\n \"name\": \"Bank Clearance Summary\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Guarantee\",\n \"name\": \"Bank Guarantee\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup cheque dimensions for printing\",\n \"label\": \"Cheque Print Template\",\n \"name\": \"Cheque Print Template\",\n \"type\": \"doctype\"\n }\n]",
+ "title": "Banking and Payments"
},
{
"hidden": 0,
@@ -103,7 +103,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
- "modified": "2020-04-01 11:28:50.925719",
+ "modified": "2020-04-29 12:17:34.844397",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index f48d6df..df6cedd 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -42,7 +42,7 @@
def get_doctypes_for_closing(self):
docs_for_closing = []
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
- "Bank Reconciliation", "Asset", "Stock Entry"]
+ "Bank Clearance", "Asset", "Stock Entry"]
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype)
diff --git a/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json b/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json
index f85bc52..e3f2d59 100644
--- a/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json
+++ b/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json
@@ -1,74 +1,32 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-04-16 21:50:05.860195",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2018-04-16 21:50:05.860195",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2018-04-20 14:00:46.014502",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Allowed To Transact With",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-01 12:32:34.044911",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Allowed To Transact With",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation/README.md b/erpnext/accounts/doctype/bank_clearance/README.md
similarity index 100%
rename from erpnext/accounts/doctype/bank_reconciliation/README.md
rename to erpnext/accounts/doctype/bank_clearance/README.md
diff --git a/erpnext/education/doctype/video/__init__.py b/erpnext/accounts/doctype/bank_clearance/__init__.py
similarity index 100%
rename from erpnext/education/doctype/video/__init__.py
rename to erpnext/accounts/doctype/bank_clearance/__init__.py
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
similarity index 96%
rename from erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
rename to erpnext/accounts/doctype/bank_clearance/bank_clearance.js
index 19fadbf..ba3f2fa 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js
@@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-frappe.ui.form.on("Bank Reconciliation", {
+frappe.ui.form.on("Bank Clearance", {
setup: function(frm) {
frm.add_fetch("account", "account_currency", "account_currency");
},
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.json b/erpnext/accounts/doctype/bank_clearance/bank_clearance.json
new file mode 100644
index 0000000..a436d1e
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.json
@@ -0,0 +1,130 @@
+{
+ "allow_copy": 1,
+ "creation": "2013-01-10 16:34:05",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+ "account",
+ "account_currency",
+ "from_date",
+ "to_date",
+ "column_break_5",
+ "bank_account",
+ "include_reconciled_entries",
+ "include_pos_transactions",
+ "get_payment_entries",
+ "section_break_10",
+ "payment_entries",
+ "update_clearance_date",
+ "total_amount"
+ ],
+ "fields": [
+ {
+ "fetch_from": "bank_account.account",
+ "fetch_if_empty": 1,
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Account",
+ "options": "Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "account_currency",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Account Currency",
+ "options": "Currency",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "From Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "To Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
+ },
+ {
+ "description": "Select the Bank Account to reconcile.",
+ "fieldname": "bank_account",
+ "fieldtype": "Link",
+ "label": "Bank Account",
+ "options": "Bank Account"
+ },
+ {
+ "default": "0",
+ "fieldname": "include_reconciled_entries",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Include Reconciled Entries"
+ },
+ {
+ "default": "0",
+ "fieldname": "include_pos_transactions",
+ "fieldtype": "Check",
+ "label": "Include POS Transactions"
+ },
+ {
+ "fieldname": "get_payment_entries",
+ "fieldtype": "Button",
+ "label": "Get Payment Entries"
+ },
+ {
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break"
+ },
+ {
+ "allow_bulk_edit": 1,
+ "fieldname": "payment_entries",
+ "fieldtype": "Table",
+ "label": "Payment Entries",
+ "options": "Bank Clearance Detail"
+ },
+ {
+ "fieldname": "update_clearance_date",
+ "fieldtype": "Button",
+ "label": "Update Clearance Date"
+ },
+ {
+ "fieldname": "total_amount",
+ "fieldtype": "Currency",
+ "label": "Total Amount",
+ "options": "account_currency",
+ "read_only": 1
+ }
+ ],
+ "hide_toolbar": 1,
+ "icon": "fa fa-check",
+ "idx": 1,
+ "issingle": 1,
+ "modified": "2020-04-06 16:12:06.628008",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Clearance",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "read": 1,
+ "role": "Accounts User",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 1,
+ "sort_field": "modified",
+ "sort_order": "ASC"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
similarity index 98%
rename from erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
rename to erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 48fd154..6fec3ab 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -11,7 +11,7 @@
"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"
}
-class BankReconciliation(Document):
+class BankClearance(Document):
def get_payment_entries(self):
if not (self.from_date and self.to_date):
frappe.throw(_("From Date and To Date are Mandatory"))
diff --git a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
new file mode 100644
index 0000000..833abde
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
@@ -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 TestBankClearance(unittest.TestCase):
+ pass
diff --git a/erpnext/accounts/doctype/bank_clearance_detail/README.md b/erpnext/accounts/doctype/bank_clearance_detail/README.md
new file mode 100644
index 0000000..ee83a44
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_clearance_detail/README.md
@@ -0,0 +1 @@
+Detail of transaction for parent Bank Clearance.
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/__init__.py b/erpnext/accounts/doctype/bank_clearance_detail/__init__.py
similarity index 100%
rename from erpnext/accounts/doctype/bank_reconciliation_detail/__init__.py
rename to erpnext/accounts/doctype/bank_clearance_detail/__init__.py
diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.json
similarity index 98%
rename from erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
rename to erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.json
index 183068c..04988bf 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.json
+++ b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.json
@@ -326,7 +326,7 @@
"modified": "2019-01-07 16:52:07.174687",
"modified_by": "Administrator",
"module": "Accounts",
- "name": "Bank Reconciliation Detail",
+ "name": "Bank Clearance Detail",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.py b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
similarity index 84%
rename from erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.py
rename to erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
index 990b2a6..ecc5367 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_detail/bank_reconciliation_detail.py
+++ b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py
@@ -5,5 +5,5 @@
import frappe
from frappe.model.document import Document
-class BankReconciliationDetail(Document):
+class BankClearanceDetail(Document):
pass
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation/__init__.py b/erpnext/accounts/doctype/bank_reconciliation/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/erpnext/accounts/doctype/bank_reconciliation/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
deleted file mode 100644
index b85ef3e..0000000
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.json
+++ /dev/null
@@ -1,484 +0,0 @@
-{
- "allow_copy": 1,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-01-10 16:34:05",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
- "fields": [
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "bank_account.account",
- "fetch_if_empty": 1,
- "fieldname": "account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "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": "account_currency",
- "fieldtype": "Link",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Account Currency",
- "length": 0,
- "no_copy": 0,
- "options": "Currency",
- "permlevel": 0,
- "print_hide": 1,
- "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": "from_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "From Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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": "to_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "To Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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_5",
- "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,
- "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,
- "description": "Select the Bank Account to reconcile.",
- "fetch_if_empty": 0,
- "fieldname": "bank_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Bank Account",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Account",
- "permlevel": 0,
- "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": "include_reconciled_entries",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Include Reconciled Entries",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "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": "include_pos_transactions",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Include POS Transactions",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "get_payment_entries",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Get Payment Entries",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "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_10",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
- {
- "allow_bulk_edit": 1,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "payment_entries",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Payment Entries",
- "length": 0,
- "no_copy": 0,
- "options": "Bank Reconciliation Detail",
- "permlevel": 0,
- "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": "update_clearance_date",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Update Clearance Date",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "permlevel": 0,
- "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": "total_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Total Amount",
- "length": 0,
- "no_copy": 0,
- "options": "account_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- }
- ],
- "has_web_view": 0,
- "hide_toolbar": 1,
- "icon": "fa fa-check",
- "idx": 1,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2020-01-22 00:00:00.000000",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Bank Reconciliation",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 1,
- "show_name_in_global_search": 0,
- "sort_order": "ASC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
-}
diff --git a/erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.js b/erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.js
deleted file mode 100644
index f52f6fb..0000000
--- a/erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.js
+++ /dev/null
@@ -1,22 +0,0 @@
-QUnit.module('Account');
-
-QUnit.test("test Bank Reconciliation", function(assert) {
- assert.expect(0);
- let done = assert.async();
- frappe.run_serially([
- () => frappe.set_route('Form', 'Bank Reconciliation'),
- () => cur_frm.set_value('bank_account','Cash - FT'),
- () => frappe.click_button('Get Payment Entries'),
- () => {
- for(var i=0;i<=cur_frm.doc.payment_entries.length-1;i++){
- cur_frm.doc.payment_entries[i].clearance_date = frappe.datetime.add_days(frappe.datetime.now_date(), 2);
- }
- },
- () => {cur_frm.refresh_fields('payment_entries');},
- () => frappe.click_button('Update Clearance Date'),
- () => frappe.timeout(0.5),
- () => frappe.click_button('Close'),
- () => done()
- ]);
-});
-
diff --git a/erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.py
deleted file mode 100644
index 932fb33..0000000
--- a/erpnext/accounts/doctype/bank_reconciliation/test_bank_reconciliation.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-import unittest
-
-class TestBankReconciliation(unittest.TestCase):
- pass
diff --git a/erpnext/accounts/doctype/bank_reconciliation_detail/README.md b/erpnext/accounts/doctype/bank_reconciliation_detail/README.md
deleted file mode 100644
index 07d0731..0000000
--- a/erpnext/accounts/doctype/bank_reconciliation_detail/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Detail of transaction for parent Bank Reconciliation.
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 9c19791..61c48c7 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -5,7 +5,7 @@
import frappe
import unittest
-from frappe.utils import nowdate
+from frappe.utils import nowdate, now_datetime
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
@@ -13,27 +13,28 @@
class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self):
- set_total_expense_zero("2013-02-28", "cost_center")
+ set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
+ "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
budget.cancel()
+ jv.cancel()
def test_monthly_budget_crossed_stop1(self):
- set_total_expense_zero("2013-02-28", "cost_center")
+ set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28")
+ "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@@ -41,14 +42,14 @@
budget.cancel()
def test_exception_approver_role(self):
- set_total_expense_zero("2013-02-28", "cost_center")
+ set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-03-02")
+ "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@@ -112,16 +113,17 @@
budget.load_from_db()
budget.cancel()
+ po.cancel()
def test_monthly_budget_crossed_stop2(self):
- set_total_expense_zero("2013-02-28", "project")
+ set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-02-28")
+ "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@@ -129,86 +131,76 @@
budget.cancel()
def test_yearly_budget_crossed_stop1(self):
- set_total_expense_zero("2013-02-28", "cost_center")
+ set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 150000, "_Test Cost Center - _TC", posting_date="2013-03-28")
+ "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
budget.cancel()
def test_yearly_budget_crossed_stop2(self):
- set_total_expense_zero("2013-02-28", "project")
+ set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 150000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-03-28")
+ "_Test Bank - _TC", 250000, "_Test Cost Center - _TC", project="_Test Project", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
budget.cancel()
def test_monthly_budget_on_cancellation1(self):
- set_total_expense_zero("2013-02-28", "cost_center")
+ set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center")
- jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
+ for i in range(now_datetime().month):
+ jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
- self.assertTrue(frappe.db.get_value("GL Entry",
- {"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
-
- jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
-
- self.assertTrue(frappe.db.get_value("GL Entry",
- {"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
+ self.assertTrue(frappe.db.get_value("GL Entry",
+ {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
- self.assertRaises(BudgetError, jv1.cancel)
+ self.assertRaises(BudgetError, jv.cancel)
budget.load_from_db()
budget.cancel()
def test_monthly_budget_on_cancellation2(self):
- set_total_expense_zero("2013-02-28", "project")
+ set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project")
- jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project")
+ for i in range(now_datetime().month):
+ jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
+ "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project")
- self.assertTrue(frappe.db.get_value("GL Entry",
- {"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
-
- jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project")
-
- self.assertTrue(frappe.db.get_value("GL Entry",
- {"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
+ self.assertTrue(frappe.db.get_value("GL Entry",
+ {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
- self.assertRaises(BudgetError, jv1.cancel)
+ self.assertRaises(BudgetError, jv.cancel)
budget.load_from_db()
budget.cancel()
def test_monthly_budget_against_group_cost_center(self):
- set_total_expense_zero("2013-02-28", "cost_center")
- set_total_expense_zero("2013-02-28", "cost_center", "_Test Cost Center 2 - _TC")
+ set_total_expense_zero(nowdate(), "cost_center")
+ set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date="2013-02-28")
+ "_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@@ -231,7 +223,7 @@
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", 40000, cost_center, posting_date="2013-02-28")
+ "_Test Bank - _TC", 40000, cost_center, posting_date=nowdate())
self.assertRaises(BudgetError, jv.submit)
@@ -246,12 +238,14 @@
else:
budget_against = budget_against_CC or "_Test Cost Center - _TC"
+ fiscal_year = get_fiscal_year(nowdate())[0]
+
args = frappe._dict({
"account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date,
"company": "_Test Company",
- "fiscal_year": "_Test Fiscal Year 2013",
+ "fiscal_year": fiscal_year,
"budget_against_field": budget_against_field,
})
@@ -263,10 +257,10 @@
if existing_expense:
if budget_against_field == "cost_center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True)
+ "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
- "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date="2013-02-28")
+ "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date=nowdate())
def make_budget(**args):
args = frappe._dict(args)
@@ -274,10 +268,13 @@
budget_against=args.budget_against
cost_center=args.cost_center
+ fiscal_year = get_fiscal_year(nowdate())[0]
+
if budget_against == "Project":
- budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", "_Test Project/_Test Fiscal Year 2013%")})
+ project_name = "{0}%".format("_Test Project/" + fiscal_year)
+ budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", project_name)})
else:
- cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/_Test Fiscal Year 2013")
+ cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)})
for d in budget_list:
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
@@ -290,8 +287,10 @@
else:
budget.cost_center =cost_center or "_Test Cost Center - _TC"
+ monthly_distribution = frappe.get_doc("Monthly Distribution", "_Test Distribution")
+ monthly_distribution.fiscal_year = fiscal_year
- budget.fiscal_year = "_Test Fiscal Year 2013"
+ budget.fiscal_year = fiscal_year
budget.monthly_distribution = "_Test Distribution"
budget.company = "_Test Company"
budget.applicable_on_booking_actual_expenses = 1
@@ -300,7 +299,7 @@
budget.budget_against = budget_against
budget.append("accounts", {
"account": "_Test Account Cost for Goods Sold - _TC",
- "budget_amount": 100000
+ "budget_amount": 200000
})
if args.applicable_on_material_request:
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js
index 96ec57d..9e2f6ee 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.js
+++ b/erpnext/accounts/doctype/cost_center/cost_center.js
@@ -18,7 +18,7 @@
},
refresh: function(frm) {
if (!frm.is_new()) {
- frm.add_custom_button(__('Update Cost Center Number'), function () {
+ frm.add_custom_button(__('Update Cost Center Name / Number'), function () {
frm.trigger("update_cost_center_number");
});
}
@@ -47,35 +47,45 @@
},
update_cost_center_number: function(frm) {
var d = new frappe.ui.Dialog({
- title: __('Update Cost Center Number'),
+ title: __('Update Cost Center Name / Number'),
fields: [
{
- "label": 'Cost Center Number',
+ "label": "Cost Center Name",
+ "fieldname": "cost_center_name",
+ "fieldtype": "Data",
+ "reqd": 1,
+ "default": frm.doc.cost_center_name
+ },
+ {
+ "label": "Cost Center Number",
"fieldname": "cost_center_number",
"fieldtype": "Data",
- "reqd": 1
+ "reqd": 1,
+ "default": frm.doc.cost_center_number
}
],
primary_action: function() {
var data = d.get_values();
- if(data.cost_center_number === frm.doc.cost_center_number) {
+ if(data.cost_center_name === frm.doc.cost_center_name && data.cost_center_number === frm.doc.cost_center_number) {
d.hide();
return;
}
+ frappe.dom.freeze();
frappe.call({
- method: "erpnext.accounts.utils.update_number_field",
+ method: "erpnext.accounts.utils.update_cost_center",
args: {
- doctype_name: frm.doc.doctype,
- name: frm.doc.name,
- field_name: d.fields[0].fieldname,
- number_value: data.cost_center_number,
+ docname: frm.doc.name,
+ cost_center_name: data.cost_center_name,
+ cost_center_number: data.cost_center_number,
company: frm.doc.company
},
callback: function(r) {
+ frappe.dom.unfreeze();
if(!r.exc) {
if(r.message) {
frappe.set_route("Form", "Cost Center", r.message);
} else {
+ me.frm.set_value("cost_center_name", data.cost_center_name);
me.frm.set_value("cost_center_number", data.cost_center_number);
}
d.hide();
diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json
index 99b89d1..5013c92 100644
--- a/erpnext/accounts/doctype/cost_center/cost_center.json
+++ b/erpnext/accounts/doctype/cost_center/cost_center.json
@@ -2,7 +2,6 @@
"actions": [],
"allow_copy": 1,
"allow_import": 1,
- "allow_rename": 1,
"creation": "2013-01-23 19:57:17",
"description": "Track separate Income and Expense for product verticals or divisions.",
"doctype": "DocType",
@@ -126,7 +125,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2020-03-18 17:59:04.321637",
+ "modified": "2020-04-29 16:09:30.025214",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cost Center",
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json
index 2214811..0d75329 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.json
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json
@@ -30,7 +30,8 @@
"company",
"finance_book",
"to_rename",
- "due_date"
+ "due_date",
+ "is_cancelled"
],
"fields": [
{
@@ -245,12 +246,18 @@
"fieldname": "due_date",
"fieldtype": "Date",
"label": "Due Date"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_cancelled",
+ "fieldtype": "Check",
+ "label": "Is Cancelled"
}
],
"icon": "fa fa-list",
"idx": 1,
"in_create": 1,
- "modified": "2020-03-28 16:22:33.766994",
+ "modified": "2020-04-07 16:22:33.766994",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 14d0531..efab580 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -30,23 +30,20 @@
self.pl_must_have_cost_center()
self.validate_cost_center()
- if not self.flags.from_repost:
- self.check_pl_account()
- self.validate_party()
- self.validate_currency()
+ self.check_pl_account()
+ self.validate_party()
+ self.validate_currency()
- def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
- if not from_repost:
- self.validate_account_details(adv_adj)
- self.validate_dimensions_for_pl_and_bs()
- check_freezing_date(self.posting_date, adv_adj)
+ def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
+ self.validate_account_details(adv_adj)
+ self.validate_dimensions_for_pl_and_bs()
validate_frozen_account(self.account, adv_adj)
validate_balance_type(self.account, adv_adj)
# Update outstanding amt on against voucher
if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
- and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
+ and self.against_voucher and update_outstanding == 'Yes':
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher)
@@ -159,7 +156,6 @@
if self.party_type and self.party:
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
-
def validate_and_set_fiscal_year(self):
if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
@@ -176,19 +172,6 @@
(balance_must_be=="Credit" and flt(balance) > 0):
frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be)))
-def check_freezing_date(posting_date, adv_adj=False):
- """
- Nobody can do GL Entries where posting date is before freezing date
- except authorized person
- """
- if not adv_adj:
- acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
- if acc_frozen_upto:
- frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
- if getdate(posting_date) <= getdate(acc_frozen_upto) \
- and not frozen_accounts_modifier in frappe.get_roles():
- frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
-
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
if party_type and party:
party_condition = " and party_type={0} and party={1}"\
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 3604b60..9a832e3 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -12,7 +12,6 @@
refresh: function(frm) {
erpnext.toggle_naming_series();
- frm.cscript.voucher_type(frm.doc);
if(frm.doc.docstatus==1) {
frm.add_custom_button(__('Ledger'), function() {
@@ -120,9 +119,78 @@
}
}
});
+ },
+
+ voucher_type: function(frm){
+
+ if(!frm.doc.company) return null;
+
+ if((!(frm.doc.accounts || []).length) || ((frm.doc.accounts || []).length === 1 && !frm.doc.accounts[0].account)) {
+ if(in_list(["Bank Entry", "Cash Entry"], frm.doc.voucher_type)) {
+ return frappe.call({
+ type: "GET",
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
+ args: {
+ "account_type": (frm.doc.voucher_type=="Bank Entry" ?
+ "Bank" : (frm.doc.voucher_type=="Cash Entry" ? "Cash" : null)),
+ "company": frm.doc.company
+ },
+ callback: function(r) {
+ if(r.message) {
+ // If default company bank account not set
+ if(!$.isEmptyObject(r.message)){
+ update_jv_details(frm.doc, [r.message]);
+ }
+ }
+ }
+ });
+ }
+ else if(frm.doc.voucher_type=="Opening Entry") {
+ return frappe.call({
+ type:"GET",
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
+ args: {
+ "company": frm.doc.company
+ },
+ callback: function(r) {
+ frappe.model.clear_table(frm.doc, "accounts");
+ if(r.message) {
+ update_jv_details(frm.doc, r.message);
+ }
+ cur_frm.set_value("is_opening", "Yes");
+ }
+ });
+ }
+ }
+ },
+
+ from_template: function(frm){
+ if (frm.doc.from_template){
+ frappe.db.get_doc("Journal Entry Template", frm.doc.from_template)
+ .then((doc) => {
+ frappe.model.clear_table(frm.doc, "accounts");
+ frm.set_value({
+ "company": doc.company,
+ "voucher_type": doc.voucher_type,
+ "naming_series": doc.naming_series,
+ "is_opening": doc.is_opening,
+ "multi_currency": doc.multi_currency
+ })
+ update_jv_details(frm.doc, doc.accounts);
+ });
+ }
}
});
+var update_jv_details = function(doc, r) {
+ $.each(r, function(i, d) {
+ var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
+ row.account = d.account;
+ row.balance = d.balance;
+ });
+ refresh_field("accounts");
+}
+
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
onload: function() {
this.load_defaults();
@@ -375,56 +443,6 @@
cur_frm.pformat.print_heading = __("Journal Entry");
}
-cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
- cur_frm.set_df_property("cheque_no", "reqd", doc.voucher_type=="Bank Entry");
- cur_frm.set_df_property("cheque_date", "reqd", doc.voucher_type=="Bank Entry");
-
- if(!doc.company) return;
-
- var update_jv_details = function(doc, r) {
- $.each(r, function(i, d) {
- var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
- row.account = d.account;
- row.balance = d.balance;
- });
- refresh_field("accounts");
- }
-
- if((!(doc.accounts || []).length) || ((doc.accounts || []).length==1 && !doc.accounts[0].account)) {
- if(in_list(["Bank Entry", "Cash Entry"], doc.voucher_type)) {
- return frappe.call({
- type: "GET",
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
- args: {
- "account_type": (doc.voucher_type=="Bank Entry" ?
- "Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)),
- "company": doc.company
- },
- callback: function(r) {
- if(r.message) {
- update_jv_details(doc, [r.message]);
- }
- }
- })
- } else if(doc.voucher_type=="Opening Entry") {
- return frappe.call({
- type:"GET",
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
- args: {
- "company": doc.company
- },
- callback: function(r) {
- frappe.model.clear_table(doc, "accounts");
- if(r.message) {
- update_jv_details(doc, r.message);
- }
- cur_frm.set_value("is_opening", "Yes")
- }
- })
- }
- }
-}
-
frappe.ui.form.on("Journal Entry Account", {
party: function(frm, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index f599124..9d50639 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-03-25 10:53:52",
@@ -10,10 +11,11 @@
"title",
"voucher_type",
"naming_series",
- "column_break1",
- "posting_date",
- "company",
"finance_book",
+ "column_break1",
+ "from_template",
+ "company",
+ "posting_date",
"2_add_edit_gl_entries",
"accounts",
"section_break99",
@@ -157,6 +159,7 @@
"in_global_search": 1,
"in_list_view": 1,
"label": "Reference Number",
+ "mandatory_depends_on": "eval:doc.voucher_type == \"Bank Entry\"",
"no_copy": 1,
"oldfieldname": "cheque_no",
"oldfieldtype": "Data",
@@ -166,6 +169,7 @@
"fieldname": "cheque_date",
"fieldtype": "Date",
"label": "Reference Date",
+ "mandatory_depends_on": "eval:doc.voucher_type == \"Bank Entry\"",
"no_copy": 1,
"oldfieldname": "cheque_date",
"oldfieldtype": "Date",
@@ -484,12 +488,22 @@
"options": "Journal Entry",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "from_template",
+ "fieldtype": "Link",
+ "label": "From Template",
+ "no_copy": 1,
+ "options": "Journal Entry Template",
+ "print_hide": 1,
+ "report_hide": 1
}
],
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
- "modified": "2020-01-16 13:05:30.634226",
+ "links": [],
+ "modified": "2020-04-29 10:55:28.240916",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index eb3017a..d6ffdb6 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -57,6 +57,7 @@
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name)
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.make_gl_entries(1)
self.update_advance_paid()
self.update_expense_claim()
@@ -594,7 +595,7 @@
for d in self.accounts:
if d.reference_type=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
- update_reimbursed_amount(doc)
+ update_reimbursed_amount(doc, jv=self.name)
def validate_expense_claim(self):
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 9552e60..26c84a6 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-02-22 01:27:39",
"doctype": "DocType",
@@ -271,7 +272,8 @@
],
"idx": 1,
"istable": 1,
- "modified": "2020-01-13 12:41:33.968025",
+ "links": [],
+ "modified": "2020-04-25 01:47:49.060128",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
@@ -280,4 +282,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/education/doctype/video/__init__.py b/erpnext/accounts/doctype/journal_entry_template/__init__.py
similarity index 100%
copy from erpnext/education/doctype/video/__init__.py
copy to erpnext/accounts/doctype/journal_entry_template/__init__.py
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
new file mode 100644
index 0000000..cbb9fc4
--- /dev/null
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js
@@ -0,0 +1,91 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on("Journal Entry Template", {
+ setup: function(frm) {
+ frappe.model.set_default_values(frm.doc);
+
+ frm.set_query("account" ,"accounts", function(){
+ var filters = {
+ company: frm.doc.company,
+ is_group: 0
+ };
+
+ if(!frm.doc.multi_currency) {
+ $.extend(filters, {
+ account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency
+ });
+ }
+
+ return { filters: filters };
+ });
+
+ frappe.call({
+ type: "GET",
+ method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series",
+ callback: function(r){
+ if(r.message){
+ frm.set_df_property("naming_series", "options", r.message.split("\n"));
+ frm.set_value("naming_series", r.message.split("\n")[0]);
+ frm.refresh_field("naming_series");
+ }
+ }
+ });
+ },
+ voucher_type: function(frm) {
+ var add_accounts = function(doc, r) {
+ $.each(r, function(i, d) {
+ var row = frappe.model.add_child(doc, "Journal Entry Template Account", "accounts");
+ row.account = d.account;
+ });
+ refresh_field("accounts");
+ };
+
+ if(!frm.doc.company) return;
+
+ frm.trigger("clear_child");
+ switch(frm.doc.voucher_type){
+ case "Opening Entry":
+ frm.set_value("is_opening", "Yes");
+ frappe.call({
+ type:"GET",
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
+ args: {
+ "company": frm.doc.company
+ },
+ callback: function(r) {
+ if(r.message) {
+ add_accounts(frm.doc, r.message);
+ }
+ }
+ });
+ break;
+ case "Bank Entry":
+ case "Cash Entry":
+ frappe.call({
+ type: "GET",
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
+ args: {
+ "account_type": (frm.doc.voucher_type=="Bank Entry" ?
+ "Bank" : (frm.doc.voucher_type=="Cash Entry" ? "Cash" : null)),
+ "company": frm.doc.company
+ },
+ callback: function(r) {
+ if(r.message) {
+ // If default company bank account not set
+ if(!$.isEmptyObject(r.message)){
+ add_accounts(frm.doc, [r.message]);
+ }
+ }
+ }
+ });
+ break;
+ default:
+ frm.trigger("clear_child");
+ }
+ },
+ clear_child: function(frm){
+ frappe.model.clear_table(frm.doc, "accounts");
+ frm.refresh_field("accounts");
+ }
+});
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json
new file mode 100644
index 0000000..660ae85
--- /dev/null
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.json
@@ -0,0 +1,134 @@
+{
+ "actions": [],
+ "autoname": "field:template_title",
+ "creation": "2020-04-09 01:32:51.332301",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "section_break_1",
+ "template_title",
+ "voucher_type",
+ "naming_series",
+ "column_break_3",
+ "company",
+ "is_opening",
+ "multi_currency",
+ "section_break_3",
+ "accounts"
+ ],
+ "fields": [
+ {
+ "fieldname": "section_break_1",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "voucher_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Journal Entry Type",
+ "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation",
+ "reqd": 1
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Company",
+ "options": "Company",
+ "remember_last_selected_value": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "No",
+ "fieldname": "is_opening",
+ "fieldtype": "Select",
+ "label": "Is Opening",
+ "options": "No\nYes"
+ },
+ {
+ "fieldname": "accounts",
+ "fieldtype": "Table",
+ "label": "Accounting Entries",
+ "options": "Journal Entry Template Account"
+ },
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "no_copy": 1,
+ "print_hide": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "template_title",
+ "fieldtype": "Data",
+ "label": "Template Title",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "multi_currency",
+ "fieldtype": "Check",
+ "label": "Multi Currency"
+ }
+ ],
+ "links": [],
+ "modified": "2020-05-01 18:32:01.420488",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Journal Entry Template",
+ "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
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Auditor",
+ "share": 1
+ }
+ ],
+ "search_fields": "voucher_type, company",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "template_title",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
new file mode 100644
index 0000000..e0b9cbc
--- /dev/null
+++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.py
@@ -0,0 +1,14 @@
+# -*- 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 JournalEntryTemplate(Document):
+ pass
+
+@frappe.whitelist()
+def get_naming_series():
+ return frappe.get_meta("Journal Entry").get_field("naming_series").options
diff --git a/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py b/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py
new file mode 100644
index 0000000..5f74a20
--- /dev/null
+++ b/erpnext/accounts/doctype/journal_entry_template/test_journal_entry_template.py
@@ -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 TestJournalEntryTemplate(unittest.TestCase):
+ pass
diff --git a/erpnext/education/doctype/video/__init__.py b/erpnext/accounts/doctype/journal_entry_template_account/__init__.py
similarity index 100%
copy from erpnext/education/doctype/video/__init__.py
copy to erpnext/accounts/doctype/journal_entry_template_account/__init__.py
diff --git a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json
new file mode 100644
index 0000000..eecd877
--- /dev/null
+++ b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.json
@@ -0,0 +1,31 @@
+{
+ "actions": [],
+ "creation": "2020-04-09 01:48:42.783620",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "account"
+ ],
+ "fields": [
+ {
+ "fieldname": "account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Account",
+ "options": "Account",
+ "reqd": 1
+ }
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-04-25 01:15:44.879839",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Journal Entry Template Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py
new file mode 100644
index 0000000..48e6abb
--- /dev/null
+++ b/erpnext/accounts/doctype/journal_entry_template_account/journal_entry_template_account.py
@@ -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 JournalEntryTemplateAccount(Document):
+ pass
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index b53e68f..bcb22f0 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -60,6 +60,7 @@
self.set_remarks()
self.validate_duplicate_entry()
self.validate_allocated_amount()
+ self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
self.set_status()
@@ -75,6 +76,7 @@
self.set_status()
def on_cancel(self):
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.setup_party_account_field()
self.make_gl_entries(cancel=1)
self.update_outstanding_amounts()
@@ -265,6 +267,25 @@
frappe.throw(_("{0} {1} must be submitted")
.format(d.reference_doctype, d.reference_name))
+ def validate_paid_invoices(self):
+ no_oustanding_refs = {}
+
+ for d in self.get("references"):
+ if not d.allocated_amount:
+ continue
+
+ if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
+ outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
+ if outstanding_amount <= 0 and not is_return:
+ no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
+
+ for k, v in no_oustanding_refs.items():
+ frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
+ If this is undesirable please cancel the corresponding Payment Entry.")
+ .format(k, frappe.bold(", ".join([d.reference_name for d in v])), frappe.bold("negative outstanding amount")),
+ title=_("Warning"), indicator="orange")
+
+
def validate_journal_entry(self):
for d in self.get("references"):
if d.allocated_amount and d.reference_doctype == "Journal Entry":
@@ -571,7 +592,7 @@
for d in self.get("references"):
if d.reference_doctype=="Expense Claim" and d.reference_name:
doc = frappe.get_doc("Expense Claim", d.reference_name)
- update_reimbursed_amount(doc)
+ update_reimbursed_amount(doc, self.name)
def on_recurring(self, reference_doc, auto_repeat_doc):
self.reference_no = reference_doc.name
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js
new file mode 100644
index 0000000..7ea60bb
--- /dev/null
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js
@@ -0,0 +1,12 @@
+frappe.listview_settings['Payment Entry'] = {
+
+ onload: function(listview) {
+ listview.page.fields_dict.party_type.get_query = function() {
+ return {
+ "filters": {
+ "name": ["in", Object.keys(frappe.boot.party_account_types)],
+ }
+ };
+ };
+ }
+};
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 4c7d933..8bb741f 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -35,8 +35,6 @@
pe.cancel()
- self.assertFalse(self.get_gle(pe.name))
-
so_advance_paid = frappe.db.get_value("Sales Order", so.name, "advance_paid")
self.assertEqual(so_advance_paid, 0)
@@ -124,7 +122,6 @@
self.assertEqual(outstanding_amount, 0)
pe.cancel()
- self.assertFalse(self.get_gle(pe.name))
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 100)
@@ -381,7 +378,6 @@
self.assertEqual(outstanding_amount, 0)
pe3.cancel()
- self.assertFalse(self.get_gle(pe3.name))
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, -100)
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 53ff222..68aeb6d 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -326,7 +326,7 @@
"reference_doctype": args.dt,
"reference_name": args.dn,
"party_type": args.get("party_type") or "Customer",
- "party": args.get("party") or ref_doc.customer,
+ "party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account
})
@@ -420,7 +420,7 @@
def update_payment_req_status(doc, method):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_reference_details
-
+
for ref in doc.references:
payment_request_name = frappe.db.get_value("Payment Request",
{"reference_doctype": ref.reference_doctype, "reference_name": ref.reference_name,
@@ -430,7 +430,7 @@
ref_details = get_reference_details(ref.reference_doctype, ref.reference_name, doc.party_account_currency)
pay_req_doc = frappe.get_doc('Payment Request', payment_request_name)
status = pay_req_doc.status
-
+
if status != "Paid" and not ref_details.outstanding_amount:
status = 'Paid'
elif status != "Partially Paid" and ref_details.outstanding_amount != ref_details.total_amount:
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js
index 03b8f93..87e02fe 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js
@@ -5,7 +5,7 @@
onload: function(frm) {
if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date());
},
-
+
setup: function(frm) {
frm.set_query("closing_account_head", function() {
return {
@@ -18,9 +18,9 @@
}
});
},
-
+
refresh: function(frm) {
- if(frm.doc.docstatus==1) {
+ if(frm.doc.docstatus > 0) {
frm.add_custom_button(__('Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
@@ -33,5 +33,5 @@
}, "fa fa-table");
}
}
-
+
})
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index eb95e45..0bd9a90 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -17,8 +17,9 @@
self.make_gl_entries()
def on_cancel(self):
- frappe.db.sql("""delete from `tabGL Entry`
- where voucher_type = 'Period Closing Voucher' and voucher_no=%s""", self.name)
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ from erpnext.accounts.general_ledger import make_reverse_gl_entries
+ make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name, cancel=True)
def validate_account_head(self):
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")
diff --git a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json
index 2fb66d2..59a673e 100644
--- a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json
+++ b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.json
@@ -1,123 +1,39 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-10-27 16:46:06.060930",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "creation": "2017-10-27 16:46:06.060930",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "default",
+ "user"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "default",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Default",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "default": "0",
+ "fieldname": "default",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Default"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "user",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "User",
- "length": 0,
- "no_copy": 0,
- "options": "User",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "user",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "User",
+ "options": "User"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2017-11-23 17:13:16.005475",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "POS Profile User",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- }
- ],
- "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2020-05-01 09:46:47.599173",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "POS Profile User",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 3cf4d59..4f6be59 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -382,11 +382,6 @@
cur_frm.refresh_fields();
}
-cur_frm.cscript.update_stock = function(doc, dt, dn) {
- hide_fields(doc, dt, dn);
- this.frm.fields_dict.items.grid.toggle_reqd("item_code", doc.update_stock? true: false)
-}
-
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
return {
filters: [
@@ -528,5 +523,10 @@
erpnext.buying.get_default_bom(frm);
}
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
+ },
+
+ update_stock: function(frm) {
+ hide_fields(frm.doc);
+ frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock? true: false);
}
})
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index b1ae194..3aa24df 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -14,7 +14,7 @@
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.stock import get_warehouse_account_map
-from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
+from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, make_reverse_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
@@ -382,7 +382,7 @@
self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
- def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
+ def make_gl_entries(self, gl_entries=None):
if not self.grand_total:
return
if not gl_entries:
@@ -391,21 +391,17 @@
if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
- make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
- update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
+ if self.docstatus == 1:
+ make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False)
+ elif self.docstatus == 2:
+ make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
- if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) and self.auto_accounting_for_stock:
- from erpnext.controllers.stock_controller import update_gl_entries_after
- items, warehouses = self.get_items_and_warehouses()
- update_gl_entries_after(self.posting_date, self.posting_time,
- warehouses, items, company = self.company)
-
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
- delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+ make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@@ -934,6 +930,7 @@
frappe.db.set(self, 'status', 'Cancelled')
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
def update_project(self):
project_list = []
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_records.json b/erpnext/accounts/doctype/purchase_invoice/test_records.json
index 171927c..7030faf 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_records.json
+++ b/erpnext/accounts/doctype/purchase_invoice/test_records.json
@@ -138,13 +138,12 @@
"row_id": 7
}
],
- "posting_date": "2013-02-03",
"supplier": "_Test Supplier",
"supplier_name": "_Test Supplier"
},
-
-
-
+
+
+
{
"bill_no": "NA",
"buying_price_list": "_Test Price List",
@@ -204,7 +203,6 @@
"tax_amount": 150.0
}
],
- "posting_date": "2013-02-03",
"supplier": "_Test Supplier",
"supplier_name": "_Test Supplier"
}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 60e41f9..f248276 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -345,7 +345,7 @@
set_dynamic_labels: function() {
this._super();
- this.hide_fields(this.frm.doc);
+ this.frm.events.hide_fields(this.frm)
},
items_on_form_rendered: function() {
@@ -404,7 +404,7 @@
if(r.message && r.message.print_format) {
me.frm.pos_print_format = r.message.print_format;
}
- me.frm.script_manager.trigger("update_stock");
+ me.frm.trigger("update_stock");
if(me.frm.doc.taxes_and_charges) {
me.frm.script_manager.trigger("taxes_and_charges");
}
@@ -446,35 +446,6 @@
// for backward compatibility: combine new and previous states
$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
-// Hide Fields
-// ------------
-cur_frm.cscript.hide_fields = function(doc) {
- var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
- 'advances', 'from_date', 'to_date'];
-
- if(cint(doc.is_pos) == 1) {
- hide_field(parent_fields);
- } else {
- for (var i in parent_fields) {
- var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
- if(!docfield.hidden) unhide_field(parent_fields[i]);
- }
- }
-
- // India related fields
- if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
- else hide_field(['c_form_applicable', 'c_form_no']);
-
- this.frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
-
- cur_frm.refresh_fields();
-}
-
-cur_frm.cscript.update_stock = function(doc, dt, dn) {
- cur_frm.cscript.hide_fields(doc, dt, dn);
- this.frm.fields_dict.items.grid.toggle_reqd("item_code", doc.update_stock? true: false)
-}
-
cur_frm.cscript['Make Delivery Note'] = function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_delivery_note",
@@ -719,6 +690,12 @@
frm.redemption_conversion_factor = null;
},
+ update_stock: function(frm, dt, dn) {
+ frm.events.hide_fields(frm);
+ frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock);
+ frm.trigger('reset_posting_time');
+ },
+
redeem_loyalty_points: function(frm) {
frm.events.get_loyalty_details(frm);
},
@@ -742,6 +719,29 @@
}
},
+ hide_fields: function(frm) {
+ let doc = frm.doc;
+ var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
+ 'advances', 'from_date', 'to_date'];
+
+ if(cint(doc.is_pos) == 1) {
+ hide_field(parent_fields);
+ } else {
+ for (var i in parent_fields) {
+ var docfield = frappe.meta.docfield_map[doc.doctype][parent_fields[i]];
+ if(!docfield.hidden) unhide_field(parent_fields[i]);
+ }
+ }
+
+ // India related fields
+ if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
+ else hide_field(['c_form_applicable', 'c_form_no']);
+
+ frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
+
+ frm.refresh_fields();
+ },
+
get_loyalty_details: function(frm) {
if (frm.doc.customer && frm.doc.redeem_loyalty_points) {
frappe.call({
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 918fa14..db20589 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -149,9 +149,9 @@
"edit_printing_settings",
"letter_head",
"group_same_items",
- "language",
- "column_break_84",
"select_print_heading",
+ "column_break_84",
+ "language",
"more_information",
"inter_company_invoice_reference",
"is_internal_customer",
@@ -1579,7 +1579,7 @@
"idx": 181,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-17 12:38:41.435728",
+ "modified": "2020-04-29 13:37:09.355300",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3c40112..3b0fade 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -7,7 +7,6 @@
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date
-from erpnext.controllers.stock_controller import update_gl_entries_after
from frappe.model.mapper import get_mapped_doc
from erpnext.accounts.doctype.sales_invoice.pos import update_multi_mode_option
@@ -282,6 +281,8 @@
if "Healthcare" in active_domains:
manage_invoice_submit_cancel(self, "on_cancel")
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.append({
@@ -717,7 +718,9 @@
if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1:
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
- def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
+ def make_gl_entries(self, gl_entries=None):
+ from erpnext.accounts.general_ledger import make_reverse_gl_entries
+
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not gl_entries:
gl_entries = self.get_gl_entries()
@@ -729,23 +732,19 @@
update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account or
cint(self.redeem_loyalty_points)) else "Yes"
- make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
- update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
+ if self.docstatus == 1:
+ make_gl_entries(gl_entries, update_outstanding=update_outstanding, merge_entries=False)
+ elif self.docstatus == 2:
+ make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
update_outstanding_amt(self.debit_to, "Customer", self.customer,
self.doctype, self.return_against if cint(self.is_return) and self.return_against else self.name)
- if (repost_future_gle or self.flags.repost_future_gle) and cint(self.update_stock) \
- and cint(auto_accounting_for_stock):
- items, warehouses = self.get_items_and_warehouses()
- update_gl_entries_after(self.posting_date, self.posting_time,
- warehouses, items, company = self.company)
elif self.docstatus == 2 and cint(self.update_stock) \
and cint(auto_accounting_for_stock):
- from erpnext.accounts.general_ledger import delete_gl_entries
- delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+ make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 88b54fe..dd727a4 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -364,7 +364,7 @@
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
- self.assertFalse(gle)
+ self.assertTrue(gle)
def test_tax_calculation_with_multiple_items(self):
si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True)
@@ -678,14 +678,15 @@
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
- self.assertFalse(gle)
+ self.assertTrue(gle)
def test_pos_gl_entry_with_perpetual_inventory(self):
make_pos_profile()
- pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
+ pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
- pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
+ pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
+ income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1
pos.update_stock = 1
@@ -766,9 +767,13 @@
def test_pos_change_amount(self):
make_pos_profile()
- pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
+ pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
+ item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
- pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
+ pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
+ debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
+ income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
+ cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1
pos.update_stock = 1
@@ -787,8 +792,15 @@
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
pos_profile = make_pos_profile()
- pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
- pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
+
+ pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
+ item_code= "_Test FG Item",
+ warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
+
+ pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
+ debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
+ income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
+ cost_center = "Main - TCP1", do_not_save=True)
pos.is_pos = 1
pos.update_stock = 1
@@ -891,11 +903,9 @@
gle = frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
- self.assertFalse(gle)
-
+ self.assertTrue(gle)
frappe.db.sql("delete from `tabPOS Profile`")
- si.delete()
def test_pos_si_without_payment(self):
set_perpetual_inventory()
@@ -1012,9 +1022,6 @@
si.cancel()
- self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
- where reference_name=%s""", si.name))
-
def test_serialized(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -1230,7 +1237,7 @@
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
- self.assertFalse(gle)
+ self.assertTrue(gle)
def test_invalid_currency(self):
# Customer currency = USD
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 5ba455c..fb1a4f4 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe, erpnext
-from frappe.utils import flt, cstr, cint, comma_and
+from frappe.utils import flt, cstr, cint, comma_and, today, getdate, formatdate, now
from frappe import _
from erpnext.accounts.utils import get_stock_and_account_balance
from frappe.model.meta import get_field_precision
@@ -15,17 +15,17 @@
class StockAccountInvalidTransaction(frappe.ValidationError): pass
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
-def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
+def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes'):
if gl_map:
if not cancel:
validate_accounting_period(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
if gl_map and len(gl_map) > 1:
- save_entries(gl_map, adv_adj, update_outstanding, from_repost)
+ save_entries(gl_map, adv_adj, update_outstanding)
else:
frappe.throw(_("Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction."))
else:
- delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
+ make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
def validate_accounting_period(gl_map):
accounting_periods = frappe.db.sql(""" SELECT
@@ -119,33 +119,36 @@
if same_head:
return e
-def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
- if not from_repost:
- validate_cwip_accounts(gl_map)
+def save_entries(gl_map, adv_adj, update_outstanding):
+ validate_cwip_accounts(gl_map)
round_off_debit_credit(gl_map)
+
+ if gl_map:
+ check_freezing_date(gl_map[0]["posting_date"], adv_adj)
+
for entry in gl_map:
- make_entry(entry, adv_adj, update_outstanding, from_repost)
+ make_entry(entry, adv_adj, update_outstanding)
# check against budget
- if not from_repost:
- validate_expense_against_budget(entry)
+ validate_expense_against_budget(entry)
- if not from_repost:
- validate_account_for_perpetual_inventory(gl_map)
+ validate_account_for_perpetual_inventory(gl_map)
-def make_entry(args, adv_adj, update_outstanding, from_repost=False):
+def make_entry(args, adv_adj, update_outstanding):
gle = frappe.new_doc("GL Entry")
gle.update(args)
gle.flags.ignore_permissions = 1
- gle.flags.from_repost = from_repost
gle.validate()
gle.db_insert()
- gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
+ gle.run_method("on_update_with_args", adv_adj, update_outstanding)
gle.flags.ignore_validate = True
gle.submit()
+ # check against budget
+ validate_expense_against_budget(args)
+
def validate_account_for_perpetual_inventory(gl_map):
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)):
account_list = [gl_entries.account for gl_entries in gl_map]
@@ -169,33 +172,33 @@
.format(account), StockAccountInvalidTransaction)
# This has been comment for a temporary, will add this code again on release of immutable ledger
- # elif account_bal != stock_bal:
- # precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
- # currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
+ elif account_bal != stock_bal:
+ precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
+ currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency"))
- # diff = flt(stock_bal - account_bal, precision)
- # error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
- # stock_bal, account_bal, frappe.bold(account))
- # error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
- # stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
+ diff = flt(stock_bal - account_bal, precision)
+ error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format(
+ stock_bal, account_bal, frappe.bold(account))
+ error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff))
+ stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account")
- # db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
- # db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
+ db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency')
+ db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency')
- # journal_entry_args = {
- # 'accounts':[
- # {'account': account, db_or_cr_warehouse_account : abs(diff)},
- # {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
- # }
+ journal_entry_args = {
+ 'accounts':[
+ {'account': account, db_or_cr_warehouse_account : abs(diff)},
+ {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }]
+ }
- # frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
- # raise_exception=StockValueAndAccountBalanceOutOfSync,
- # title=_('Values Out Of Sync'),
- # primary_action={
- # 'label': _('Make Journal Entry'),
- # 'client_action': 'erpnext.route_to_adjustment_jv',
- # 'args': journal_entry_args
- # })
+ frappe.msgprint(msg="""{0}<br></br>{1}<br></br>""".format(error_reason, error_resolution),
+ raise_exception=StockValueAndAccountBalanceOutOfSync,
+ title=_('Values Out Of Sync'),
+ primary_action={
+ 'label': _('Make Journal Entry'),
+ 'client_action': 'erpnext.route_to_adjustment_jv',
+ 'args': journal_entry_args
+ })
def validate_cwip_accounts(gl_map):
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
@@ -282,31 +285,64 @@
return round_off_account, round_off_cost_center
-def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
- adv_adj=False, update_outstanding="Yes"):
-
- from erpnext.accounts.doctype.gl_entry.gl_entry import validate_balance_type, \
- check_freezing_date, update_outstanding_amt, validate_frozen_account
+def make_reverse_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
+ adv_adj=False, update_outstanding="Yes"):
+ """
+ Get original gl entries of the voucher
+ and make reverse gl entries by swapping debit and credit
+ """
if not gl_entries:
- gl_entries = frappe.db.sql("""
- select account, posting_date, party_type, party, cost_center, fiscal_year,voucher_type,
- voucher_no, against_voucher_type, against_voucher, cost_center, company
- from `tabGL Entry`
- where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), as_dict=True)
+ gl_entries = frappe.get_all("GL Entry",
+ fields = ["*"],
+ filters = {
+ "voucher_type": voucher_type,
+ "voucher_no": voucher_no
+ })
if gl_entries:
+ set_as_cancel(gl_entries[0]['voucher_type'], gl_entries[0]['voucher_no'])
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
- frappe.db.sql("""delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s""",
- (voucher_type or gl_entries[0]["voucher_type"], voucher_no or gl_entries[0]["voucher_no"]))
+ for entry in gl_entries:
+ entry['name'] = None
+ debit = entry.get('debit', 0)
+ credit = entry.get('credit', 0)
- for entry in gl_entries:
- validate_frozen_account(entry["account"], adv_adj)
- validate_balance_type(entry["account"], adv_adj)
- if not adv_adj:
- validate_expense_against_budget(entry)
+ debit_in_account_currency = entry.get('debit_in_account_currency', 0)
+ credit_in_account_currency = entry.get('credit_in_account_currency', 0)
- if entry.get("against_voucher") and update_outstanding == 'Yes' and not adv_adj:
- update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
- entry.get("against_voucher"), on_cancel=True)
+ entry['debit'] = credit
+ entry['credit'] = debit
+ entry['debit_in_account_currency'] = credit_in_account_currency
+ entry['credit_in_account_currency'] = debit_in_account_currency
+
+ entry['remarks'] = "On cancellation of " + entry['voucher_no']
+ entry['is_cancelled'] = 1
+ entry['posting_date'] = today()
+
+ if entry['debit'] or entry['credit']:
+ make_entry(entry, adv_adj, "Yes")
+
+
+def check_freezing_date(posting_date, adv_adj=False):
+ """
+ Nobody can do GL Entries where posting date is before freezing date
+ except authorized person
+ """
+ if not adv_adj:
+ acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
+ if acc_frozen_upto:
+ frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
+ if getdate(posting_date) <= getdate(acc_frozen_upto) \
+ and not frozen_accounts_modifier in frappe.get_roles():
+ frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
+
+def set_as_cancel(voucher_type, voucher_no):
+ """
+ Set is_cancelled=1 in all original gl entries for the voucher
+ """
+ frappe.db.sql("""update `tabGL Entry` set is_cancelled = 1,
+ modified=%s, modified_by=%s
+ where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
+ (now(), frappe.session.user, voucher_type, voucher_no))
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 47dfa09..6e5b33f 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -600,10 +600,12 @@
else:
return ''
-def get_partywise_advanced_payment_amount(party_type, posting_date = None):
+def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None):
cond = "1=1"
if posting_date:
cond = "posting_date <= '{0}'".format(posting_date)
+ if company:
+ cond += "and company = '{0}'".format(company)
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
FROM `tabGL Entry`
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index b607c0f..aa6b42e 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -33,7 +33,7 @@
self.get_party_total(args)
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
- self.filters.report_date) or {}
+ self.filters.report_date, self.filters.company) or {}
for party, party_dict in iteritems(self.party_total):
if party_dict.outstanding == 0:
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index b62238b..c2c7207 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -84,6 +84,7 @@
def get_profit_loss_data(fiscal_year, companies, columns, filters):
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
+ company_currency = get_company_currency(filters)
data = []
data.extend(income or [])
@@ -93,7 +94,7 @@
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
- report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, True)
+ report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True)
return data, None, chart, report_summary
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index ac49d37..1188bea 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -154,8 +154,12 @@
{
"fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"),
- "fieldtype": "Check",
- "default": 1
+ "fieldtype": "Check"
+ },
+ {
+ "fieldname": "show_cancelled_entries",
+ "label": __("Show Cancelled Entries"),
+ "fieldtype": "Check"
}
]
}
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index f776d93..7af5fa8 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -188,6 +188,9 @@
else:
conditions.append("finance_book in (%(finance_book)s)")
+ if not filters.get("show_cancelled_entries"):
+ conditions.append("is_cancelled = 0")
+
from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("GL Entry")
diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
index 260f35f..1c45810 100644
--- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
+++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
@@ -35,6 +35,12 @@
})
return columns, data
+ # to avoid error eg: gross_income[0] : list index out of range
+ if not gross_income:
+ gross_income = [{}]
+ if not gross_expense:
+ gross_expense = [{}]
+
data.append({
"account_name": "'" + _("Included in Gross Profit") + "'",
"account": "'" + _("Included in Gross Profit") + "'"
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index 127f313..1f78c7a 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -102,7 +102,7 @@
data.append(row)
- if filters.get('group_by'):
+ if filters.get('group_by') and item_list:
total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
data.append(total_row)
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 0c8957a..92a22e6 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -111,7 +111,7 @@
data.append(row)
- if filters.get('group_by'):
+ if filters.get('group_by') and item_list:
total_row = total_row_map.get(prev_group_by_value or d.get('item_name'))
total_row['percent_gt'] = flt(total_row['total']/grand_total * 100)
data.append(total_row)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 4789063..5165495 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -817,48 +817,37 @@
pass
@frappe.whitelist()
-def update_number_field(doctype_name, name, field_name, number_value, company):
+def update_cost_center(docname, cost_center_name, cost_center_number, company):
'''
- doctype_name = Name of the DocType
- name = Docname being referred
- field_name = Name of the field thats holding the 'number' attribute
- number_value = Numeric value entered in field_name
-
- Stores the number entered in the dialog to the DocType's field.
-
Renames the document by adding the number as a prefix to the current name and updates
all transaction where it was present.
'''
- doc_title = frappe.db.get_value(doctype_name, name, frappe.scrub(doctype_name)+"_name")
+ validate_field_number("Cost Center", docname, cost_center_number, company, "cost_center_number")
- validate_field_number(doctype_name, name, number_value, company, field_name)
+ if cost_center_number:
+ frappe.db.set_value("Cost Center", docname, "cost_center_number", cost_center_number.strip())
+ else:
+ frappe.db.set_value("Cost Center", docname, "cost_center_number", "")
- frappe.db.set_value(doctype_name, name, field_name, number_value)
+ frappe.db.set_value("Cost Center", docname, "cost_center_name", cost_center_name.strip())
- if doc_title[0].isdigit():
- separator = " - " if " - " in doc_title else " "
- doc_title = doc_title.split(separator, 1)[1]
-
- frappe.db.set_value(doctype_name, name, frappe.scrub(doctype_name)+"_name", doc_title)
-
- new_name = get_autoname_with_number(number_value, doc_title, name, company)
-
- if name != new_name:
- frappe.rename_doc(doctype_name, name, new_name)
+ new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company)
+ if docname != new_name:
+ frappe.rename_doc("Cost Center", docname, new_name, force=1)
return new_name
-def validate_field_number(doctype_name, name, number_value, company, field_name):
+def validate_field_number(doctype_name, docname, number_value, company, field_name):
''' Validate if the number entered isn't already assigned to some other document. '''
if number_value:
+ filters = {field_name: number_value, "name": ["!=", docname]}
if company:
- doctype_with_same_number = frappe.db.get_value(doctype_name,
- {field_name: number_value, "company": company, "name": ["!=", name]})
- else:
- doctype_with_same_number = frappe.db.get_value(doctype_name,
- {field_name: number_value, "name": ["!=", name]})
+ filters["company"] = company
+
+ doctype_with_same_number = frappe.db.get_value(doctype_name, filters)
+
if doctype_with_same_number:
- frappe.throw(_("{0} Number {1} already used in account {2}")
- .format(doctype_name, number_value, doctype_with_same_number))
+ frappe.throw(_("{0} Number {1} is already used in {2} {3}")
+ .format(doctype_name, number_value, doctype_name.lower(), doctype_with_same_number))
def get_autoname_with_number(number_value, doc_title, name, company):
''' append title with prefix as number and suffix as company's abbreviation separated by '-' '''
@@ -897,4 +886,60 @@
return frappe.get_all("Account", filters = {
"account_type": "Stock",
"company": company
- })
\ No newline at end of file
+ })
+
+def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
+ warehouse_account=None, company=None):
+ def _delete_gl_entries(voucher_type, voucher_no):
+ frappe.db.sql("""delete from `tabGL Entry`
+ where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
+
+ if not warehouse_account:
+ warehouse_account = get_warehouse_account_map(company)
+
+ future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
+ gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
+
+ for voucher_type, voucher_no in future_stock_vouchers:
+ existing_gle = gle.get((voucher_type, voucher_no), [])
+ voucher_obj = frappe.get_doc(voucher_type, voucher_no)
+ expected_gle = voucher_obj.get_gl_entries(warehouse_account)
+ if expected_gle:
+ if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
+ _delete_gl_entries(voucher_type, voucher_no)
+ voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
+ else:
+ _delete_gl_entries(voucher_type, voucher_no)
+
+def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
+ future_stock_vouchers = []
+
+ values = []
+ condition = ""
+ if for_items:
+ condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
+ values += for_items
+
+ if for_warehouses:
+ condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
+ values += for_warehouses
+
+ for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
+ from `tabStock Ledger Entry` sle
+ where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
+ order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
+ tuple([posting_date, posting_time] + values), as_dict=True):
+ future_stock_vouchers.append([d.voucher_type, d.voucher_no])
+
+ return future_stock_vouchers
+
+def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
+ gl_entries = {}
+ if future_stock_vouchers:
+ for d in frappe.db.sql("""select * from `tabGL Entry`
+ where posting_date >= %s and voucher_no in (%s)""" %
+ ('%s', ', '.join(['%s']*len(future_stock_vouchers))),
+ tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
+ gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
+
+ return gl_entries
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 759d42a..ecbfeb7 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -11,7 +11,7 @@
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_depreciation_accounts
-from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
+from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
from erpnext.accounts.utils import get_account_currency
from erpnext.controllers.accounts_controller import AccountsController
@@ -41,7 +41,8 @@
self.validate_cancellation()
self.delete_depreciation_entries()
self.set_status()
- delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ make_reverse_gl_entries(voucher_type='Asset', voucher_no=self.name)
self.db_set('booked_fixed_asset', 0)
def validate_asset_and_reference(self):
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index a56440d..050b30d 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -66,9 +66,6 @@
pr.cancel()
self.assertEqual(asset.docstatus, 2)
- self.assertFalse(frappe.db.get_value("GL Entry",
- {"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
-
def test_is_fixed_asset_set(self):
asset = create_asset(is_existing_asset = 1)
doc = frappe.new_doc('Purchase Invoice')
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index 9bf4df4..9a33fc1 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -11,7 +11,8 @@
class AssetCategory(Document):
def validate(self):
self.validate_finance_books()
- self.validate_accounts()
+ self.validate_account_types()
+ self.validate_account_currency()
def validate_finance_books(self):
for d in self.finance_books:
@@ -19,7 +20,26 @@
if cint(d.get(frappe.scrub(field)))<1:
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
- def validate_accounts(self):
+ def validate_account_currency(self):
+ account_types = [
+ 'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account'
+ ]
+ invalid_accounts = []
+ for d in self.accounts:
+ company_currency = frappe.get_value('Company', d.get('company_name'), 'default_currency')
+ for type_of_account in account_types:
+ if d.get(type_of_account):
+ account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency")
+ if account_currency != company_currency:
+ invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) }))
+
+ for d in invalid_accounts:
+ frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.")
+ .format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)),
+ title=_("Invalid Account"))
+
+
+ def validate_account_types(self):
account_type_map = {
'fixed_asset_account': { 'account_type': 'Fixed Asset' },
'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' },
diff --git a/erpnext/buying/doctype/supplier/supplier_dashboard.py b/erpnext/buying/doctype/supplier/supplier_dashboard.py
index d0d5b73..1625103 100644
--- a/erpnext/buying/doctype/supplier/supplier_dashboard.py
+++ b/erpnext/buying/doctype/supplier/supplier_dashboard.py
@@ -23,15 +23,11 @@
},
{
'label': _('Payments'),
- 'items': ['Payment Entry']
- },
- {
- 'label': _('Bank'),
- 'items': ['Bank Account']
+ 'items': ['Payment Entry', 'Bank Account']
},
{
'label': _('Pricing'),
'items': ['Pricing Rule']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py
index 08711fc..b9b0da4 100644
--- a/erpnext/config/accounts.py
+++ b/erpnext/config/accounts.py
@@ -183,8 +183,8 @@
},
{
"type": "doctype",
- "label": _("Update Bank Transaction Dates"),
- "name": "Bank Reconciliation",
+ "label": _("Update Bank Clearance Dates"),
+ "name": "Bank Clearance",
"description": _("Update bank payment dates with journals.")
},
{
diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py
index 7b3b466..9855a11 100644
--- a/erpnext/config/hr.py
+++ b/erpnext/config/hr.py
@@ -172,6 +172,10 @@
},
{
"type": "doctype",
+ "name": "Income Tax Slab",
+ },
+ {
+ "type": "doctype",
"name": "Salary Component",
},
{
@@ -211,6 +215,10 @@
},
{
"type": "doctype",
+ "name": "Employee Other Income",
+ },
+ {
+ "type": "doctype",
"name": "Employee Benefit Application",
"dependencies": ["Employee"]
},
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 3e97f76..eecb143 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -664,23 +664,26 @@
def set_total_advance_paid(self):
if self.doctype == "Sales Order":
dr_or_cr = "credit_in_account_currency"
+ rev_dr_or_cr = "debit_in_account_currency"
party = self.customer
else:
dr_or_cr = "debit_in_account_currency"
+ rev_dr_or_cr = "credit_in_account_currency"
party = self.supplier
advance = frappe.db.sql("""
select
- account_currency, sum({dr_or_cr}) as amount
+ account_currency, sum({dr_or_cr}) - sum({rev_dr_cr}) as amount
from
`tabGL Entry`
where
against_voucher_type = %s and against_voucher = %s and party=%s
and docstatus = 1
- """.format(dr_or_cr=dr_or_cr), (self.doctype, self.name, party), as_dict=1)
+ """.format(dr_or_cr=dr_or_cr, rev_dr_cr=rev_dr_or_cr), (self.doctype, self.name, party), as_dict=1) #nosec
if advance:
advance = advance[0]
+
advance_paid = flt(advance.amount, self.precision("advance_paid"))
formatted_advance_paid = fmt_money(advance_paid, precision=self.precision("advance_paid"),
currency=advance.account_currency)
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 9d453af..86de808 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -7,7 +7,7 @@
from frappe import _
import frappe.defaults
from erpnext.accounts.utils import get_fiscal_year
-from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
+from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
@@ -23,9 +23,9 @@
self.validate_serialized_batch()
self.validate_customer_provided_item()
- def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
+ def make_gl_entries(self, gl_entries=None):
if self.docstatus == 2:
- delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+ make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(erpnext.is_perpetual_inventory_enabled(self.company)):
warehouse_account = get_warehouse_account_map(self.company)
@@ -33,16 +33,12 @@
if self.docstatus==1:
if not gl_entries:
gl_entries = self.get_gl_entries(warehouse_account)
- make_gl_entries(gl_entries, from_repost=from_repost)
+ make_gl_entries(gl_entries)
- if (repost_future_gle or self.flags.repost_future_gle):
- items, warehouses = self.get_items_and_warehouses()
- update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
- warehouse_account, company=self.company)
elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1:
gl_entries = []
gl_entries = self.get_asset_gl_entry(gl_entries)
- make_gl_entries(gl_entries, from_repost=from_repost)
+ make_gl_entries(gl_entries)
def validate_serialized_batch(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -274,21 +270,21 @@
"batch_no": cstr(d.get("batch_no")).strip(),
"serial_no": d.get("serial_no"),
"project": d.get("project") or self.get('project'),
- "is_cancelled": self.docstatus==2 and "Yes" or "No"
+ "is_cancelled": 1 if self.docstatus==2 else 0
})
sl_dict.update(args)
return sl_dict
- def make_sl_entries(self, sl_entries, is_amended=None, allow_negative_stock=False,
+ def make_sl_entries(self, sl_entries, allow_negative_stock=False,
via_landed_cost_voucher=False):
from erpnext.stock.stock_ledger import make_sl_entries
- make_sl_entries(sl_entries, is_amended, allow_negative_stock, via_landed_cost_voucher)
+ make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher)
- def make_gl_entries_on_cancel(self, repost_future_gle=True):
+ def make_gl_entries_on_cancel(self):
if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
and voucher_no=%s""", (self.doctype, self.name)):
- self.make_gl_entries(repost_future_gle=repost_future_gle)
+ self.make_gl_entries()
def get_serialized_items(self):
serialized_items = []
@@ -391,29 +387,6 @@
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
-def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
- warehouse_account=None, company=None):
- def _delete_gl_entries(voucher_type, voucher_no):
- frappe.db.sql("""delete from `tabGL Entry`
- where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
-
- if not warehouse_account:
- warehouse_account = get_warehouse_account_map(company)
-
- future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
- gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
-
- for voucher_type, voucher_no in future_stock_vouchers:
- existing_gle = gle.get((voucher_type, voucher_no), [])
- voucher_obj = frappe.get_doc(voucher_type, voucher_no)
- expected_gle = voucher_obj.get_gl_entries(warehouse_account)
- if expected_gle:
- if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
- _delete_gl_entries(voucher_type, voucher_no)
- voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
- else:
- _delete_gl_entries(voucher_type, voucher_no)
-
def compare_existing_and_expected_gle(existing_gle, expected_gle):
matched = True
for entry in expected_gle:
@@ -430,36 +403,3 @@
matched = False
break
return matched
-
-def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
- future_stock_vouchers = []
-
- values = []
- condition = ""
- if for_items:
- condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
- values += for_items
-
- if for_warehouses:
- condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
- values += for_warehouses
-
- for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
- from `tabStock Ledger Entry` sle
- where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
- order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
- tuple([posting_date, posting_time] + values), as_dict=True):
- future_stock_vouchers.append([d.voucher_type, d.voucher_no])
-
- return future_stock_vouchers
-
-def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
- gl_entries = {}
- if future_stock_vouchers:
- for d in frappe.db.sql("""select * from `tabGL Entry`
- where posting_date >= %s and voucher_no in (%s)""" %
- ('%s', ', '.join(['%s']*len(future_stock_vouchers))),
- tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
- gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
-
- return gl_entries
diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py
new file mode 100644
index 0000000..38bf79e
--- /dev/null
+++ b/erpnext/crm/utils.py
@@ -0,0 +1,24 @@
+import frappe
+
+
+def update_lead_phone_numbers(contact, method):
+ if contact.phone_nos:
+ contact_lead = contact.get_link_for("Lead")
+ if contact_lead:
+ phone = mobile_no = contact.phone_nos[0].phone
+
+ if len(contact.phone_nos) > 1:
+ # get the default phone number
+ primary_phones = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_phone]
+ if primary_phones:
+ phone = primary_phones[0]
+
+ # get the default mobile number
+ primary_mobile_nos = [phone_doc.phone for phone_doc in contact.phone_nos if phone_doc.is_primary_mobile_no]
+ if primary_mobile_nos:
+ mobile_no = primary_mobile_nos[0]
+
+ lead = frappe.get_doc("Lead", contact_lead)
+ lead.phone = phone
+ lead.mobile_no = mobile_no
+ lead.save()
diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js
index e2c6f1d..17ef449 100644
--- a/erpnext/education/doctype/fees/fees.js
+++ b/erpnext/education/doctype/fees/fees.js
@@ -112,6 +112,8 @@
args: {
"dt": frm.doc.doctype,
"dn": frm.doc.name,
+ "party_type": "Student",
+ "party": frm.doc.student,
"recipient_id": frm.doc.student_email
},
callback: function(r) {
diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py
index aa616e6..01f7b87 100644
--- a/erpnext/education/doctype/fees/fees.py
+++ b/erpnext/education/doctype/fees/fees.py
@@ -10,7 +10,7 @@
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from frappe.utils.csvutils import getlink
from erpnext.controllers.accounts_controller import AccountsController
-from erpnext.accounts.general_ledger import delete_gl_entries
+from erpnext.accounts.general_ledger import make_reverse_gl_entries
class Fees(AccountsController):
@@ -75,12 +75,14 @@
self.make_gl_entries()
if self.send_payment_request and self.student_email:
- pr = make_payment_request(dt="Fees", dn=self.name, recipient_id=self.student_email,
+ pr = make_payment_request(party_type="Student", party=self.student, dt="Fees",
+ dn=self.name, recipient_id=self.student_email,
submit_doc=True, use_dummy_message=True)
frappe.msgprint(_("Payment request {0} created").format(getlink("Payment Request", pr.name)))
def on_cancel(self):
- delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name, cancel=True)
# frappe.db.set(self, 'status', 'Cancelled')
diff --git a/erpnext/education/doctype/student/student_dashboard.py b/erpnext/education/doctype/student/student_dashboard.py
index 0cbd17b..d261462 100644
--- a/erpnext/education/doctype/student/student_dashboard.py
+++ b/erpnext/education/doctype/student/student_dashboard.py
@@ -6,6 +6,9 @@
'heatmap': True,
'heatmap_message': _('This is based on the attendance of this Student'),
'fieldname': 'student',
+ 'non_standard_fieldnames': {
+ 'Bank Account': 'party'
+ },
'transactions': [
{
'label': _('Admission'),
@@ -29,7 +32,7 @@
},
{
'label': _('Fee'),
- 'items': ['Fees']
+ 'items': ['Fees', 'Bank Account']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/education/doctype/video/test_video.js b/erpnext/education/doctype/video/test_video.js
deleted file mode 100644
index a82a221..0000000
--- a/erpnext/education/doctype/video/test_video.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Video", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Video
- () => frappe.tests.make('Video', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/education/doctype/video/test_video.py b/erpnext/education/doctype/video/test_video.py
deleted file mode 100644
index ecb09a2..0000000
--- a/erpnext/education/doctype/video/test_video.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import unittest
-
-class TestVideo(unittest.TestCase):
- pass
diff --git a/erpnext/education/doctype/video/video.js b/erpnext/education/doctype/video/video.js
deleted file mode 100644
index c35c19b..0000000
--- a/erpnext/education/doctype/video/video.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Video', {
- refresh: function(frm) {
-
- }
-});
diff --git a/erpnext/education/doctype/video/video.json b/erpnext/education/doctype/video/video.json
deleted file mode 100644
index e912eb3..0000000
--- a/erpnext/education/doctype/video/video.json
+++ /dev/null
@@ -1,112 +0,0 @@
-{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:title",
- "creation": "2018-10-17 05:47:13.087395",
- "doctype": "DocType",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "title",
- "provider",
- "url",
- "column_break_4",
- "publish_date",
- "duration",
- "section_break_7",
- "description"
- ],
- "fields": [
- {
- "fieldname": "title",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Title",
- "reqd": 1,
- "unique": 1
- },
- {
- "fieldname": "description",
- "fieldtype": "Text Editor",
- "in_list_view": 1,
- "label": "Description",
- "reqd": 1
- },
- {
- "fieldname": "duration",
- "fieldtype": "Data",
- "label": "Duration"
- },
- {
- "fieldname": "url",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "URL",
- "reqd": 1
- },
- {
- "fieldname": "publish_date",
- "fieldtype": "Date",
- "label": "Publish Date"
- },
- {
- "fieldname": "provider",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Provider",
- "options": "YouTube\nVimeo",
- "reqd": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "section_break_7",
- "fieldtype": "Section Break"
- }
- ],
- "modified": "2019-06-12 12:36:48.753092",
- "modified_by": "Administrator",
- "module": "Education",
- "name": "Video",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Academics User",
- "share": 1,
- "write": 1
- },
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Instructor",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "LMS User",
- "share": 1
- }
- ],
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1
-}
\ No newline at end of file
diff --git a/erpnext/education/doctype/video/video.py b/erpnext/education/doctype/video/video.py
deleted file mode 100644
index b19f812..0000000
--- a/erpnext/education/doctype/video/video.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.model.document import Document
-
-class Video(Document):
-
-
- def get_video(self):
- pass
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
index 5f36bdd..87c22cc 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
@@ -80,6 +80,7 @@
frappe.call({
method: 'complete_procedure',
doc: frm.doc,
+ freeze: true,
callback: function(r) {
if (r.message) {
frappe.show_alert({
@@ -87,8 +88,8 @@
['<a class="bold" href="#Form/Stock Entry/'+ r.message + '">' + r.message + '</a>']),
indicator: 'green'
});
- frm.reload_doc();
}
+ frm.reload_doc();
}
});
}
@@ -111,9 +112,10 @@
frappe.call({
doc: frm.doc,
method: 'make_material_receipt',
+ freeze: true,
callback: function(r) {
if (!r.exc) {
- cur_frm.reload_doc();
+ frm.reload_doc();
let doclist = frappe.model.sync(r.message);
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
}
@@ -122,7 +124,7 @@
}
);
} else {
- cur_frm.reload_doc();
+ frm.reload_doc();
}
}
}
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
index db3afc8..b7d7a62 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
@@ -87,7 +87,8 @@
else:
frappe.throw(_('Please set Customer in Patient {0}').format(frappe.bold(self.patient)), title=_('Customer Not Found'))
- frappe.db.set_value('Clinical Procedure', self.name, 'status', 'Completed')
+ self.db_set('status', 'Completed')
+
if self.consume_stock and self.items:
return stock_entry
@@ -245,9 +246,9 @@
def insert_clinical_procedure_to_medical_record(doc):
- subject = cstr(doc.procedure_template)
+ subject = frappe.bold(_("Clinical Procedure conducted: ")) + cstr(doc.procedure_template) + "<br>"
if doc.practitioner:
- subject += ' ' + doc.practitioner
+ subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner
if subject and doc.notes:
subject += '<br/>' + doc.notes
diff --git a/erpnext/healthcare/doctype/exercise_type/exercise_type.js b/erpnext/healthcare/doctype/exercise_type/exercise_type.js
index f450c9b..68db047 100644
--- a/erpnext/healthcare/doctype/exercise_type/exercise_type.js
+++ b/erpnext/healthcare/doctype/exercise_type/exercise_type.js
@@ -24,6 +24,8 @@
this.exercise_cards = $('<div class="exercise-cards"></div>').appendTo(this.wrapper);
+ this.row = $('<div class="exercise-row"></div>').appendTo(this.wrapper);
+
let me = this;
this.exercise_toolbar.find(".btn-add")
@@ -32,7 +34,7 @@
me.show_add_card_dialog(frm);
});
- if (frm.doc.steps_table.length > 0) {
+ if (frm.doc.steps_table && frm.doc.steps_table.length > 0) {
this.make_cards(frm);
this.make_buttons(frm);
}
@@ -41,7 +43,6 @@
make_cards: function(frm) {
var me = this;
$(me.exercise_cards).empty();
- this.row = $('<div class="exercise-row"></div>').appendTo(me.exercise_cards);
$.each(frm.doc.steps_table, function(i, step) {
$(repl(`
@@ -78,6 +79,7 @@
frm.doc.steps_table.pop(id);
frm.refresh_field('steps_table');
$('#col-'+id).remove();
+ frm.dirty();
}, 300);
});
},
@@ -106,7 +108,10 @@
],
primary_action: function() {
let data = d.get_values();
- let i = frm.doc.steps_table.length;
+ let i = 0;
+ if (frm.doc.steps_table) {
+ i = frm.doc.steps_table.length;
+ }
$(repl(`
<div class="exercise-col col-sm-4" id="%(col_id)s">
<div class="card h-100 exercise-card" id="%(card_id)s">
@@ -165,9 +170,10 @@
frm.doc.steps_table[id].image = data.image;
frm.doc.steps_table[id].description = data.step_description;
refresh_field('steps_table');
+ frm.dirty();
new_dialog.hide();
},
- primary_action_label: __("Save"),
+ primary_action_label: __("Edit"),
});
new_dialog.set_values({
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py
index 4e4015d..ea8ce25 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.py
@@ -288,23 +288,23 @@
table_row = False
subject = cstr(doc.lab_test_name)
if doc.practitioner:
- subject += " "+ doc.practitioner
+ subject += frappe.bold(_("Healthcare Practitioner: "))+ doc.practitioner + "<br>"
if doc.normal_test_items:
item = doc.normal_test_items[0]
comment = ""
if item.lab_test_comment:
comment = str(item.lab_test_comment)
- table_row = item.lab_test_name
+ table_row = frappe.bold(_("Lab Test Conducted: ")) + item.lab_test_name
if item.lab_test_event:
- table_row += " " + item.lab_test_event
+ table_row += frappe.bold(_("Lab Test Event: ")) + item.lab_test_event
if item.result_value:
- table_row += " " + item.result_value
+ table_row += " " + frappe.bold(_("Lab Test Result: ")) + item.result_value
if item.normal_range:
- table_row += " normal_range("+item.normal_range+")"
- table_row += " "+comment
+ table_row += " " + _("Normal Range:") + item.normal_range
+ table_row += " " + comment
elif doc.special_test_items:
item = doc.special_test_items[0]
@@ -316,12 +316,12 @@
item = doc.sensitivity_test_items[0]
if item.antibiotic and item.antibiotic_sensitivity:
- table_row = item.antibiotic +" "+ item.antibiotic_sensitivity
+ table_row = item.antibiotic + " " + item.antibiotic_sensitivity
if table_row:
- subject += "<br/>"+table_row
+ subject += "<br>" + table_row
if doc.lab_test_comment:
- subject += "<br/>"+ cstr(doc.lab_test_comment)
+ subject += "<br>" + cstr(doc.lab_test_comment)
medical_record = frappe.new_doc("Patient Medical Record")
medical_record.patient = doc.patient
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
index 767643b..1734c28 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
@@ -18,6 +18,9 @@
def after_insert(self):
insert_encounter_to_medical_record(self)
+ def on_submit(self):
+ update_encounter_medical_record(self)
+
def on_cancel(self):
if self.appointment:
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Open')
@@ -66,22 +69,26 @@
frappe.db.delete_doc_if_exists('Patient Medical Record', 'reference_name', encounter.name)
def set_subject_field(encounter):
- subject = encounter.practitioner + '\n'
+ subject = frappe.bold(_('Healthcare Practitioner: ')) + encounter.practitioner + '<br>'
if encounter.symptoms:
- subject += _('Symptoms: ') + cstr(encounter.symptoms) + '\n'
+ subject += frappe.bold(_('Symptoms: ')) + '<br>'
+ for entry in encounter.symptoms:
+ subject += cstr(entry.complaint) + '<br>'
else:
- subject += _('No Symptoms') + '\n'
+ subject += frappe.bold(_('No Symptoms')) + '<br>'
if encounter.diagnosis:
- subject += _('Diagnosis: ') + cstr(encounter.diagnosis) + '\n'
+ subject += frappe.bold(_('Diagnosis: ')) + '<br>'
+ for entry in encounter.diagnosis:
+ subject += cstr(entry.diagnosis) + '<br>'
else:
- subject += _('No Diagnosis') + '\n'
+ subject += frappe.bold(_('No Diagnosis')) + '<br>'
if encounter.drug_prescription:
- subject += '\n' + _('Drug(s) Prescribed.')
+ subject += '<br>' + _('Drug(s) Prescribed.')
if encounter.lab_test_prescription:
- subject += '\n' + _('Test(s) Prescribed.')
+ subject += '<br>' + _('Test(s) Prescribed.')
if encounter.procedure_prescription:
- subject += '\n' + _('Procedure(s) Prescribed.')
+ subject += '<br>' + _('Procedure(s) Prescribed.')
return subject
diff --git a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
index 3655e24..ed82355 100644
--- a/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
+++ b/erpnext/healthcare/doctype/patient_medical_record/patient_medical_record.json
@@ -57,7 +57,7 @@
},
{
"fieldname": "subject",
- "fieldtype": "Small Text",
+ "fieldtype": "Text Editor",
"ignore_xss_filter": 1,
"label": "Subject"
},
@@ -125,7 +125,7 @@
],
"in_create": 1,
"links": [],
- "modified": "2020-03-23 19:26:59.308383",
+ "modified": "2020-04-29 12:26:57.679402",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Medical Record",
diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
index 201264f..c19be17 100644
--- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
+++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
@@ -21,8 +21,14 @@
self.status = 'Completed'
def set_totals(self):
- total_sessions = sum([int(d.no_of_sessions) for d in self.get('therapy_plan_details')])
- total_sessions_completed = sum([int(d.sessions_completed) for d in self.get('therapy_plan_details')])
+ total_sessions = 0
+ total_sessions_completed = 0
+ for entry in self.therapy_plan_details:
+ if entry.no_of_sessions:
+ total_sessions += entry.no_of_sessions
+ if entry.sessions_completed:
+ total_sessions_completed += entry.sessions_completed
+
self.db_set('total_sessions', total_sessions)
self.db_set('total_sessions_completed', total_sessions_completed)
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.js b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
index bb67575..abe4def 100644
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.js
+++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
@@ -13,23 +13,92 @@
refresh: function(frm) {
if (!frm.doc.__islocal) {
- let target = 0;
- let completed = 0;
- $.each(frm.doc.exercises, function(_i, e) {
- target += e.counts_target;
- completed += e.counts_completed;
- });
- frm.dashboard.add_indicator(__('Counts Targetted: {0}', [target]), 'blue');
- frm.dashboard.add_indicator(__('Counts Completed: {0}', [completed]), (completed < target) ? 'orange' : 'green');
+ frm.dashboard.add_indicator(__('Counts Targeted: {0}', [frm.doc.total_counts_targeted]), 'blue');
+ frm.dashboard.add_indicator(__('Counts Completed: {0}', [frm.doc.total_counts_completed]),
+ (frm.doc.total_counts_completed < frm.doc.total_counts_targeted) ? 'orange' : 'green');
}
if (frm.doc.docstatus === 1) {
- frm.add_custom_button(__('Patient Assessment'),function() {
+ frm.add_custom_button(__('Patient Assessment'), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.healthcare.doctype.patient_assessment.patient_assessment.create_patient_assessment',
frm: frm,
})
}, 'Create');
+
+ frm.add_custom_button(__('Sales Invoice'), function() {
+ frappe.model.open_mapped_doc({
+ method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.invoice_therapy_session',
+ frm: frm,
+ })
+ }, 'Create');
+ }
+ },
+
+ patient: function(frm) {
+ if (frm.doc.patient) {
+ frappe.call({
+ 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
+ args: {
+ patient: frm.doc.patient
+ },
+ callback: function (data) {
+ let age = '';
+ if (data.message.dob) {
+ age = calculate_age(data.message.dob);
+ } else if (data.message.age) {
+ age = data.message.age;
+ if (data.message.age_as_on) {
+ age = __('{0} as on {1}', [age, data.message.age_as_on]);
+ }
+ }
+ frm.set_value('patient_age', age);
+ frm.set_value('gender', data.message.sex);
+ frm.set_value('patient_name', data.message.patient_name);
+ }
+ });
+ } else {
+ frm.set_value('patient_age', '');
+ frm.set_value('gender', '');
+ frm.set_value('patient_name', '');
+ }
+ },
+
+ appointment: function(frm) {
+ if (frm.doc.appointment) {
+ frappe.call({
+ 'method': 'frappe.client.get',
+ args: {
+ doctype: 'Patient Appointment',
+ name: frm.doc.appointment
+ },
+ callback: function(data) {
+ let values = {
+ 'patient':data.message.patient,
+ 'therapy_type': data.message.therapy_type,
+ 'therapy_plan': data.message.therapy_plan,
+ 'practitioner': data.message.practitioner,
+ 'department': data.message.department,
+ 'start_date': data.message.appointment_date,
+ 'start_time': data.message.appointment_time,
+ 'service_unit': data.message.service_unit,
+ 'company': data.message.company
+ };
+ frm.set_value(values);
+ }
+ });
+ } else {
+ let values = {
+ 'patient': '',
+ 'therapy_type': '',
+ 'therapy_plan': '',
+ 'practitioner': '',
+ 'department': '',
+ 'start_date': '',
+ 'start_time': '',
+ 'service_unit': '',
+ };
+ frm.set_value(values);
}
},
@@ -44,6 +113,8 @@
callback: function(data) {
frm.set_value('duration', data.message.default_duration);
frm.set_value('rate', data.message.rate);
+ frm.set_value('service_unit', data.message.healthcare_service_unit);
+ frm.set_value('department', data.message.medical_department);
frm.doc.exercises = [];
$.each(data.message.exercises, function(_i, e) {
let exercise = frm.add_child('exercises');
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.json b/erpnext/healthcare/doctype/therapy_session/therapy_session.json
index 5ff7196..00d74a0 100644
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.json
+++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.json
@@ -9,9 +9,11 @@
"naming_series",
"appointment",
"patient",
+ "patient_name",
"patient_age",
"gender",
"column_break_5",
+ "company",
"therapy_plan",
"therapy_type",
"practitioner",
@@ -20,7 +22,6 @@
"duration",
"rate",
"location",
- "company",
"column_break_12",
"service_unit",
"start_date",
@@ -28,6 +29,10 @@
"invoiced",
"exercises_section",
"exercises",
+ "section_break_23",
+ "total_counts_targeted",
+ "column_break_25",
+ "total_counts_completed",
"amended_from"
],
"fields": [
@@ -159,7 +164,8 @@
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
- "options": "Company"
+ "options": "Company",
+ "reqd": 1
},
{
"default": "0",
@@ -173,11 +179,38 @@
"fieldtype": "Data",
"label": "Patient Age",
"read_only": 1
+ },
+ {
+ "fieldname": "total_counts_targeted",
+ "fieldtype": "Int",
+ "label": "Total Counts Targeted",
+ "read_only": 1
+ },
+ {
+ "fieldname": "total_counts_completed",
+ "fieldtype": "Int",
+ "label": "Total Counts Completed",
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_23",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_25",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "patient.patient_name",
+ "fieldname": "patient_name",
+ "fieldtype": "Data",
+ "label": "Patient Name",
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-04-21 13:16:46.378798",
+ "modified": "2020-04-29 16:49:16.286006",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Therapy Session",
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.py b/erpnext/healthcare/doctype/therapy_session/therapy_session.py
index 45d2ee6..9650183 100644
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.py
+++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.py
@@ -6,10 +6,17 @@
import frappe
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
+from frappe import _
+from frappe.utils import cstr, getdate
+from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
class TherapySession(Document):
+ def validate(self):
+ self.set_total_counts()
+
def on_submit(self):
self.update_sessions_count_in_therapy_plan()
+ insert_session_medical_record(self)
def on_cancel(self):
self.update_sessions_count_in_therapy_plan(on_cancel=True)
@@ -24,6 +31,18 @@
entry.sessions_completed += 1
therapy_plan.save()
+ def set_total_counts(self):
+ target_total = 0
+ counts_completed = 0
+ for entry in self.exercises:
+ if entry.counts_target:
+ target_total += entry.counts_target
+ if entry.counts_completed:
+ counts_completed += entry.counts_completed
+
+ self.db_set('total_counts_targeted', target_total)
+ self.db_set('total_counts_completed', counts_completed)
+
@frappe.whitelist()
def create_therapy_session(source_name, target_doc=None):
@@ -52,4 +71,62 @@
}
}, target_doc, set_missing_values)
- return doc
\ No newline at end of file
+ return doc
+
+
+@frappe.whitelist()
+def invoice_therapy_session(source_name, target_doc=None):
+ def set_missing_values(source, target):
+ target.customer = frappe.db.get_value('Patient', source.patient, 'customer')
+ target.due_date = getdate()
+ target.debit_to = get_receivable_account(source.company)
+ item = target.append('items', {})
+ item = get_therapy_item(source, item)
+ target.set_missing_values(for_validate=True)
+
+ doc = get_mapped_doc('Therapy Session', source_name, {
+ 'Therapy Session': {
+ 'doctype': 'Sales Invoice',
+ 'field_map': [
+ ['patient', 'patient'],
+ ['referring_practitioner', 'practitioner'],
+ ['company', 'company'],
+ ['due_date', 'start_date']
+ ]
+ }
+ }, target_doc, set_missing_values)
+
+ return doc
+
+
+def get_therapy_item(therapy, item):
+ item.item_code = frappe.db.get_value('Therapy Type', therapy.therapy_type, 'item')
+ item.description = _('Therapy Session Charges: {0}').format(therapy.practitioner)
+ item.income_account = get_income_account(therapy.practitioner, therapy.company)
+ item.cost_center = frappe.get_cached_value('Company', therapy.company, 'cost_center')
+ item.rate = therapy.rate
+ item.amount = therapy.rate
+ item.qty = 1
+ item.reference_dt = 'Therapy Session'
+ item.reference_dn = therapy.name
+ return item
+
+
+def insert_session_medical_record(doc):
+ subject = frappe.bold(_('Therapy: ')) + cstr(doc.therapy_type) + '<br>'
+ if doc.therapy_plan:
+ subject += frappe.bold(_('Therapy Plan: ')) + cstr(doc.therapy_plan) + '<br>'
+ if doc.practitioner:
+ subject += frappe.bold(_('Healthcare Practitioner: ')) + doc.practitioner
+ subject += frappe.bold(_('Total Counts Targeted: ')) + cstr(doc.total_counts_targeted) + '<br>'
+ subject += frappe.bold(_('Total Counts Completed: ')) + cstr(doc.total_counts_completed) + '<br>'
+
+ medical_record = frappe.new_doc('Patient Medical Record')
+ medical_record.patient = doc.patient
+ medical_record.subject = subject
+ medical_record.status = 'Open'
+ medical_record.communication_date = doc.start_date
+ medical_record.reference_doctype = 'Therapy Session'
+ medical_record.reference_name = doc.name
+ medical_record.reference_owner = doc.owner
+ medical_record.save(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
index 959e850..b0e78e8 100644
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py
+++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
@@ -35,17 +35,17 @@
def set_subject_field(doc):
subject = ''
- if(doc.temperature):
- subject += _('Temperature: ') + '\n'+ cstr(doc.temperature) + '. '
- if(doc.pulse):
- subject += _('Pulse: ') + '\n' + cstr(doc.pulse) + '. '
- if(doc.respiratory_rate):
- subject += _('Respiratory Rate: ') + '\n' + cstr(doc.respiratory_rate) + '. '
- if(doc.bp):
- subject += _('BP: ') + '\n' + cstr(doc.bp) + '. '
- if(doc.bmi):
- subject += _('BMI: ') + '\n' + cstr(doc.bmi) + '. '
- if(doc.nutrition_note):
- subject += _('Note: ') + '\n' + cstr(doc.nutrition_note) + '. '
+ if doc.temperature:
+ subject += frappe.bold(_('Temperature: ')) + cstr(doc.temperature) + '<br>'
+ if doc.pulse:
+ subject += frappe.bold(_('Pulse: ')) + cstr(doc.pulse) + '<br>'
+ if doc.respiratory_rate:
+ subject += frappe.bold(_('Respiratory Rate: ')) + cstr(doc.respiratory_rate) + '<br>'
+ if doc.bp:
+ subject += frappe.bold(_('BP: ')) + cstr(doc.bp) + '<br>'
+ if doc.bmi:
+ subject += frappe.bold(_('BMI: ')) + cstr(doc.bmi) + '<br>'
+ if doc.nutrition_note:
+ subject += frappe.bold(_('Note: ')) + cstr(doc.nutrition_note) + '<br>'
return subject
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index e6f6c8e..6b198e7 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -250,7 +250,8 @@
},
"Contact": {
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
- "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
+ "after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information",
+ "validate": "erpnext.crm.utils.update_lead_phone_numbers"
},
"Lead": {
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information"
@@ -537,4 +538,4 @@
{'doctype': 'Hotel Room Package', 'index': 3},
{'doctype': 'Hotel Room Type', 'index': 4}
]
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json
index 743aa23..22aa170 100644
--- a/erpnext/hr/desk_page/hr/hr.json
+++ b/erpnext/hr/desk_page/hr/hr.json
@@ -23,7 +23,7 @@
{
"hidden": 0,
"label": "Payroll",
- "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]"
+ "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@@ -73,7 +73,7 @@
{
"hidden": 0,
"label": "Employee Tax and Benefits",
- "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\",\n \"Payroll Period\"\n ],\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Modules",
@@ -88,7 +88,7 @@
"idx": 0,
"is_standard": 1,
"label": "HR",
- "modified": "2020-04-01 11:28:50.860012",
+ "modified": "2020-04-29 20:29:22.114309",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index 11ad83b..0203332 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -6,6 +6,9 @@
'heatmap': True,
'heatmap_message': _('This is based on the attendance of this Employee'),
'fieldname': 'employee',
+ 'non_standard_fieldnames': {
+ 'Bank Account': 'party'
+ },
'transactions': [
{
'label': _('Leave and Attendance'),
@@ -33,7 +36,7 @@
},
{
'label': _('Payroll'),
- 'items': ['Salary Structure Assignment', 'Salary Slip', 'Additional Salary', 'Timesheet','Employee Incentive', 'Retention Bonus']
+ 'items': ['Salary Structure Assignment', 'Salary Slip', 'Additional Salary', 'Timesheet','Employee Incentive', 'Retention Bonus', 'Bank Account']
},
{
'label': _('Training'),
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index ad9d86b..ac1bfa1 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -76,6 +76,7 @@
def on_cancel(self):
self.update_task_and_project()
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
if self.payable_account:
self.make_gl_entries(cancel=True)
@@ -260,10 +261,17 @@
if not expense.default_account or not validate:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
-def update_reimbursed_amount(doc):
- amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
+def update_reimbursed_amount(doc, jv=None):
+
+ condition = ""
+
+ if jv:
+ condition += "and voucher_no = '{0}'".format(jv)
+
+ amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) - ifnull(sum(credit_in_account_currency), 0)as amt
from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
- and party = %s """, (doc.name, doc.employee) ,as_dict=1)[0].amt
+ and party = %s {condition}""".format(condition=condition), #nosec
+ (doc.name, doc.employee) ,as_dict=1)[0].amt
doc.total_amount_reimbursed = amt
frappe.db.set_value("Expense Claim", doc.name , "total_amount_reimbursed", amt)
diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
index 6d89b19..f74315f 100644
--- a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
+++ b/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json
@@ -80,6 +80,7 @@
},
{
"collapsible": 1,
+ "collapsible_depends_on": "other_taxes_and_charges",
"fieldname": "taxes_and_charges_on_income_tax_section",
"fieldtype": "Section Break",
"label": "Taxes and Charges on Income Tax"
@@ -93,13 +94,15 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2020-04-24 12:28:36.805904",
+ "modified": "2020-04-29 15:08:21.436120",
"modified_by": "Administrator",
"module": "HR",
"name": "Income Tax Slab",
"owner": "Administrator",
"permissions": [
{
+ "amend": 1,
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -109,9 +112,11 @@
"report": 1,
"role": "System Manager",
"share": 1,
+ "submit": 1,
"write": 1
},
{
+ "amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
@@ -126,6 +131,7 @@
"write": 1
},
{
+ "amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
@@ -138,20 +144,6 @@
"share": 1,
"submit": 1,
"write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Administrator",
- "share": 1,
- "submit": 1,
- "write": 1
}
],
"sort_field": "modified",
diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json
index cdb1add..74707a2 100644
--- a/erpnext/hr/doctype/leave_application/leave_application.json
+++ b/erpnext/hr/doctype/leave_application/leave_application.json
@@ -1,332 +1,337 @@
{
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2013-02-20 11:18:11",
- "description": "Apply / Approve Leaves",
- "doctype": "DocType",
- "document_type": "Document",
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "employee",
- "employee_name",
- "column_break_4",
- "leave_type",
- "department",
- "leave_balance",
- "section_break_5",
- "from_date",
- "to_date",
- "half_day",
- "half_day_date",
- "total_leave_days",
- "column_break1",
- "description",
- "section_break_7",
- "leave_approver",
- "leave_approver_name",
- "column_break_18",
- "status",
- "salary_slip",
- "sb10",
- "posting_date",
- "follow_via_email",
- "color",
- "column_break_17",
- "company",
- "letter_head",
- "amended_from"
- ],
- "fields": [
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "no_copy": 1,
- "options": "HR-LAP-.YYYY.-",
- "print_hide": 1,
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "employee",
- "fieldtype": "Link",
- "in_global_search": 1,
- "in_standard_filter": 1,
- "label": "Employee",
- "options": "Employee",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "employee_name",
- "fieldtype": "Data",
- "in_global_search": 1,
- "label": "Employee Name",
- "read_only": 1
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "leave_type",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Leave Type",
- "options": "Leave Type",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fetch_from": "employee.department",
- "fieldname": "department",
- "fieldtype": "Link",
- "label": "Department",
- "options": "Department",
- "read_only": 1
- },
- {
- "fieldname": "leave_balance",
- "fieldtype": "Float",
- "label": "Leave Balance Before Application",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "section_break_5",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "from_date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "From Date",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fieldname": "to_date",
- "fieldtype": "Date",
- "label": "To Date",
- "reqd": 1,
- "search_index": 1
- },
- {
- "default": "0",
- "fieldname": "half_day",
- "fieldtype": "Check",
- "label": "Half Day"
- },
- {
- "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)",
- "fieldname": "half_day_date",
- "fieldtype": "Date",
- "label": "Half Day Date"
- },
- {
- "fieldname": "total_leave_days",
- "fieldtype": "Float",
- "in_list_view": 1,
- "label": "Total Leave Days",
- "no_copy": 1,
- "precision": "1",
- "read_only": 1
- },
- {
- "fieldname": "column_break1",
- "fieldtype": "Column Break",
- "print_width": "50%",
- "width": "50%"
- },
- {
- "fieldname": "description",
- "fieldtype": "Small Text",
- "label": "Reason"
- },
- {
- "fieldname": "section_break_7",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "leave_approver",
- "fieldtype": "Link",
- "label": "Leave Approver",
- "options": "User"
- },
- {
- "fieldname": "leave_approver_name",
- "fieldtype": "Data",
- "label": "Leave Approver Name",
- "read_only": 1
- },
- {
- "fieldname": "column_break_18",
- "fieldtype": "Column Break"
- },
- {
- "default": "Open",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_standard_filter": 1,
- "label": "Status",
- "no_copy": 1,
- "options": "Open\nApproved\nRejected\nCancelled"
- },
- {
- "fieldname": "sb10",
- "fieldtype": "Section Break"
- },
- {
- "default": "Today",
- "fieldname": "posting_date",
- "fieldtype": "Date",
- "label": "Posting Date",
- "no_copy": 1,
- "reqd": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
- "remember_last_selected_value": 1,
- "reqd": 1
- },
- {
- "allow_on_submit": 1,
- "default": "1",
- "fieldname": "follow_via_email",
- "fieldtype": "Check",
- "label": "Follow via Email",
- "print_hide": 1
- },
- {
- "fieldname": "column_break_17",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "salary_slip",
- "fieldtype": "Link",
- "label": "Salary Slip",
- "options": "Salary Slip",
- "print_hide": 1
- },
- {
- "allow_on_submit": 1,
- "fieldname": "letter_head",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Letter Head",
- "options": "Letter Head",
- "print_hide": 1
- },
- {
- "allow_on_submit": 1,
- "fieldname": "color",
- "fieldtype": "Color",
- "label": "Color",
- "print_hide": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Amended From",
- "no_copy": 1,
- "options": "Leave Application",
- "print_hide": 1,
- "read_only": 1
- }
- ],
- "icon": "fa fa-calendar",
- "idx": 1,
- "is_submittable": 1,
- "max_attachments": 3,
- "modified": "2019-08-13 13:32:04.860848",
- "modified_by": "Administrator",
- "module": "HR",
- "name": "Leave Application",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Employee",
- "share": 1,
- "write": 1
- },
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "HR Manager",
- "set_user_permissions": 1,
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "permlevel": 1,
- "read": 1,
- "role": "All"
- },
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "HR User",
- "set_user_permissions": 1,
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "amend": 1,
- "cancel": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Leave Approver",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "permlevel": 1,
- "read": 1,
- "report": 1,
- "role": "HR User",
- "write": 1
- },
- {
- "permlevel": 1,
- "read": 1,
- "report": 1,
- "role": "Leave Approver",
- "write": 1
- }
- ],
- "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days",
- "sort_field": "modified",
- "sort_order": "DESC",
- "timeline_field": "employee",
- "title_field": "employee_name"
- }
\ No newline at end of file
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2013-02-20 11:18:11",
+ "description": "Apply / Approve Leaves",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "employee",
+ "employee_name",
+ "column_break_4",
+ "leave_type",
+ "department",
+ "leave_balance",
+ "section_break_5",
+ "from_date",
+ "to_date",
+ "half_day",
+ "half_day_date",
+ "total_leave_days",
+ "column_break1",
+ "description",
+ "section_break_7",
+ "leave_approver",
+ "leave_approver_name",
+ "column_break_18",
+ "status",
+ "salary_slip",
+ "sb10",
+ "posting_date",
+ "follow_via_email",
+ "color",
+ "column_break_17",
+ "company",
+ "letter_head",
+ "amended_from"
+ ],
+ "fields": [
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "no_copy": 1,
+ "options": "HR-LAP-.YYYY.-",
+ "print_hide": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "in_global_search": 1,
+ "in_standard_filter": 1,
+ "label": "Employee",
+ "options": "Employee",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "employee_name",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Employee Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "leave_type",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "in_standard_filter": 1,
+ "label": "Leave Type",
+ "options": "Leave Type",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fetch_from": "employee.department",
+ "fieldname": "department",
+ "fieldtype": "Link",
+ "label": "Department",
+ "options": "Department",
+ "read_only": 1
+ },
+ {
+ "fieldname": "leave_balance",
+ "fieldtype": "Float",
+ "label": "Leave Balance Before Application",
+ "no_copy": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "From Date",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "label": "To Date",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "half_day",
+ "fieldtype": "Check",
+ "label": "Half Day"
+ },
+ {
+ "depends_on": "eval:doc.half_day && (doc.from_date != doc.to_date)",
+ "fieldname": "half_day_date",
+ "fieldtype": "Date",
+ "label": "Half Day Date"
+ },
+ {
+ "fieldname": "total_leave_days",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Total Leave Days",
+ "no_copy": 1,
+ "precision": "1",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break",
+ "print_width": "50%",
+ "width": "50%"
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "label": "Reason"
+ },
+ {
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "leave_approver",
+ "fieldtype": "Link",
+ "label": "Leave Approver",
+ "options": "User"
+ },
+ {
+ "fieldname": "leave_approver_name",
+ "fieldtype": "Data",
+ "label": "Leave Approver Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "Open",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_standard_filter": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Open\nApproved\nRejected\nCancelled",
+ "permlevel": 1
+ },
+ {
+ "fieldname": "sb10",
+ "fieldtype": "Section Break"
+ },
+ {
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "label": "Posting Date",
+ "no_copy": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1,
+ "remember_last_selected_value": 1,
+ "reqd": 1,
+ "fetch_from": "employee.company"
+ },
+ {
+ "allow_on_submit": 1,
+ "default": "1",
+ "fieldname": "follow_via_email",
+ "fieldtype": "Check",
+ "label": "Follow via Email",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "column_break_17",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "salary_slip",
+ "fieldtype": "Link",
+ "label": "Salary Slip",
+ "options": "Salary Slip",
+ "print_hide": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "letter_head",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Letter Head",
+ "options": "Letter Head",
+ "print_hide": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "color",
+ "fieldtype": "Color",
+ "label": "Color",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Leave Application",
+ "print_hide": 1,
+ "read_only": 1
+ }
+ ],
+ "icon": "fa fa-calendar",
+ "idx": 1,
+ "is_submittable": 1,
+ "links": [],
+ "max_attachments": 3,
+ "modified": "2020-03-10 22:40:43.487721",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Leave Application",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "set_user_permissions": 1,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "permlevel": 1,
+ "read": 1,
+ "role": "All"
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "set_user_permissions": 1,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Leave Approver",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "permlevel": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "write": 1
+ },
+ {
+ "permlevel": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Leave Approver",
+ "write": 1
+ }
+ ],
+ "search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "timeline_field": "employee",
+ "title_field": "employee_name"
+}
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
index d25eb6d..da25d75 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js
@@ -249,7 +249,7 @@
let make_bank_entry = function (frm) {
var doc = frm.doc;
- if (doc.company && doc.start_date && doc.end_date && doc.payment_account) {
+ if (doc.payment_account) {
return frappe.call({
doc: cur_frm.doc,
method: "make_payment_entry",
@@ -262,7 +262,8 @@
freeze_message: __("Creating Payment Entries......")
});
} else {
- frappe.msgprint(__("Company, Payment Account, From Date and To Date is mandatory"));
+ frappe.msgprint(__("Payment Account is mandatory"));
+ frm.scroll_to_field('payment_account');
}
};
diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/hr/doctype/salary_component/salary_component.json
index 5487e1d..97c46c8 100644
--- a/erpnext/hr/doctype/salary_component/salary_component.json
+++ b/erpnext/hr/doctype/salary_component/salary_component.json
@@ -227,7 +227,7 @@
{
"default": "0",
"depends_on": "eval:doc.type == \"Deduction\" && !doc.variable_based_on_taxable_salary",
- "description": "If checked, the full amount will be deducted from taxable income before calculating income tax. Otherwise, it can be exempted via Employee Tax Exemption Declaration.",
+ "description": "If checked, the full amount will be deducted from taxable income before calculating income tax without any declaration or proof submission.",
"fieldname": "exempted_from_income_tax",
"fieldtype": "Check",
"label": "Exempted from Income Tax"
@@ -235,7 +235,7 @@
],
"icon": "fa fa-flag",
"links": [],
- "modified": "2020-04-24 14:50:28.994054",
+ "modified": "2020-04-28 15:46:45.252945",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index 46600a4..f934302 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -550,15 +550,16 @@
remaining_sub_periods = get_period_factor(self.employee,
self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1]
# get taxable_earnings, paid_taxes for previous period
- previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date)
+ previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date,
+ self.start_date, tax_slab.allow_tax_exemption)
previous_total_paid_taxes = self.get_tax_paid_in_period(payroll_period.start_date, self.start_date, tax_component)
# get taxable_earnings for current period (all days)
- current_taxable_earnings = self.get_taxable_earnings()
+ current_taxable_earnings = self.get_taxable_earnings(tax_slab.allow_tax_exemption)
future_structured_taxable_earnings = current_taxable_earnings.taxable_earnings * (math.ceil(remaining_sub_periods) - 1)
# get taxable_earnings, addition_earnings for current actual payment days
- current_taxable_earnings_for_payment_days = self.get_taxable_earnings(based_on_payment_days=1)
+ current_taxable_earnings_for_payment_days = self.get_taxable_earnings(tax_slab.allow_tax_exemption, based_on_payment_days=1)
current_structured_taxable_earnings = current_taxable_earnings_for_payment_days.taxable_earnings
current_additional_earnings = current_taxable_earnings_for_payment_days.additional_income
current_additional_earnings_with_full_tax = current_taxable_earnings_for_payment_days.additional_income_with_full_tax
@@ -617,7 +618,7 @@
return income_tax_slab_doc
- def get_taxable_earnings_for_prev_period(self, start_date, end_date):
+ def get_taxable_earnings_for_prev_period(self, start_date, end_date, allow_tax_exemption=False):
taxable_earnings = frappe.db.sql("""
select sum(sd.amount)
from
@@ -637,24 +638,26 @@
})
taxable_earnings = flt(taxable_earnings[0][0]) if taxable_earnings else 0
- exempted_amount = frappe.db.sql("""
- select sum(sd.amount)
- from
- `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
- where
- sd.parentfield='deductions'
- and sd.exempted_from_income_tax=1
- and is_flexible_benefit=0
- and ss.docstatus=1
- and ss.employee=%(employee)s
- and ss.start_date between %(from_date)s and %(to_date)s
- and ss.end_date between %(from_date)s and %(to_date)s
- """, {
- "employee": self.employee,
- "from_date": start_date,
- "to_date": end_date
- })
- exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0
+ exempted_amount = 0
+ if allow_tax_exemption:
+ exempted_amount = frappe.db.sql("""
+ select sum(sd.amount)
+ from
+ `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
+ where
+ sd.parentfield='deductions'
+ and sd.exempted_from_income_tax=1
+ and is_flexible_benefit=0
+ and ss.docstatus=1
+ and ss.employee=%(employee)s
+ and ss.start_date between %(from_date)s and %(to_date)s
+ and ss.end_date between %(from_date)s and %(to_date)s
+ """, {
+ "employee": self.employee,
+ "from_date": start_date,
+ "to_date": end_date
+ })
+ exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0
return taxable_earnings - exempted_amount
@@ -682,7 +685,7 @@
return total_tax_paid
- def get_taxable_earnings(self, based_on_payment_days=0):
+ def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days=0):
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
@@ -716,12 +719,13 @@
else:
taxable_earnings += amount
- for ded in self.deductions:
- if ded.exempted_from_income_tax:
- amount = ded.amount
- if based_on_payment_days:
- amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0]
- taxable_earnings -= flt(amount)
+ if allow_tax_exemption:
+ for ded in self.deductions:
+ if ded.exempted_from_income_tax:
+ amount = ded.amount
+ if based_on_payment_days:
+ amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0]
+ taxable_earnings -= flt(amount)
return frappe._dict({
"taxable_earnings": taxable_earnings,
@@ -823,13 +827,13 @@
for slab in tax_slab.slabs:
if slab.condition and not self.eval_tax_slab_condition(slab.condition, data):
continue
- if not slab.to_amount and annual_taxable_earning > slab.from_amount:
- tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01
+ if not slab.to_amount and annual_taxable_earning >= slab.from_amount:
+ tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01
continue
- if annual_taxable_earning > slab.from_amount and annual_taxable_earning < slab.to_amount:
- tax_amount += (annual_taxable_earning - slab.from_amount) * slab.percent_deduction *.01
- elif annual_taxable_earning > slab.from_amount and annual_taxable_earning > slab.to_amount:
- tax_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01
+ if annual_taxable_earning >= slab.from_amount and annual_taxable_earning < slab.to_amount:
+ tax_amount += (annual_taxable_earning - slab.from_amount + 1) * slab.percent_deduction *.01
+ elif annual_taxable_earning >= slab.from_amount and annual_taxable_earning >= slab.to_amount:
+ tax_amount += (slab.to_amount - slab.from_amount + 1) * slab.percent_deduction * .01
# other taxes and charges on income tax
for d in tax_slab.other_taxes_and_charges:
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py
index df76458..5ba7f1c 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -149,7 +149,7 @@
return salary_structures_assignments
@frappe.whitelist()
-def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
+def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0, ignore_permissions=False):
def postprocess(source, target):
if employee:
employee_details = frappe.db.get_value("Employee", employee,
@@ -169,7 +169,7 @@
"name": "salary_structure"
}
}
- }, target_doc, postprocess, ignore_child_tables=True)
+ }, target_doc, postprocess, ignore_child_tables=True, ignore_permissions=ignore_permissions)
if cint(as_print):
doc.name = 'Preview for {0}'.format(employee)
diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
index b55b45f..9a9e42e 100644
--- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
+++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py
@@ -13,7 +13,7 @@
conditions, filters = get_conditions(filters)
columns = get_columns(filters)
att_map = get_attendance_list(conditions, filters)
- emp_map = get_employee_details()
+ emp_map = get_employee_details(filters)
holiday_list = [emp_map[d]["holiday_list"] for d in emp_map if emp_map[d]["holiday_list"]]
default_holiday_list = frappe.get_cached_value('Company', filters.get("company"), "default_holiday_list")
@@ -140,10 +140,10 @@
return conditions, filters
-def get_employee_details():
+def get_employee_details(filters):
emp_map = frappe._dict()
for d in frappe.db.sql("""select name, employee_name, designation, department, branch, company,
- holiday_list from tabEmployee""", as_dict=1):
+ holiday_list from tabEmployee where company = %s""", (filters.get("company")), as_dict=1):
emp_map.setdefault(d.name, d)
return emp_map
diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
index e5622b7..eab58ff 100644
--- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
+++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py
@@ -12,7 +12,8 @@
columns, data, chart = [], [], []
if filters.get('fiscal_year'):
company = erpnext.get_default_company()
- period_list = get_period_list(filters.get('fiscal_year'), filters.get('fiscal_year'),"Monthly", company)
+ period_list = get_period_list(filters.get('fiscal_year'), filters.get('fiscal_year'),
+ '', '', 'Fiscal Year', 'Monthly', company=company)
columns=get_columns()
data=get_log_data(filters)
chart=get_chart_data(data,period_list)
diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan_management/loan_management.json
index 691d2c1..f9ea978 100644
--- a/erpnext/loan_management/desk_page/loan_management/loan_management.json
+++ b/erpnext/loan_management/desk_page/loan_management/loan_management.json
@@ -37,7 +37,7 @@
"idx": 0,
"is_standard": 1,
"label": "Loan Management",
- "modified": "2020-04-01 11:28:51.380509",
+ "modified": "2020-04-02 11:28:51.380509",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Management",
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 2d1ad33..90b8534 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -236,7 +236,7 @@
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
- repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(get_last_day(nowdate()), 5),
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(nowdate(), 5),
"Regular Payment", 89768.75)
repayment_entry.submit()
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
index 2d9c45d..c437a98 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.json
@@ -7,17 +7,17 @@
"engine": "InnoDB",
"field_order": [
"against_loan",
- "disbursement_date",
"posting_date",
+ "applicant_type",
"column_break_4",
"company",
- "applicant_type",
"applicant",
"section_break_7",
+ "disbursement_date",
+ "column_break_8",
"disbursed_amount",
"accounting_dimensions_section",
"cost_center",
- "section_break_13",
"customer_details_section",
"bank_account",
"amended_from"
@@ -66,6 +66,7 @@
"read_only": 1
},
{
+ "collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
@@ -89,12 +90,8 @@
},
{
"fieldname": "section_break_7",
- "fieldtype": "Section Break"
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_13",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Disbursement Details"
},
{
"fieldname": "customer_details_section",
@@ -114,11 +111,15 @@
"fieldtype": "Link",
"label": "Bank Account",
"options": "Bank Account"
+ },
+ {
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break"
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-04-09 14:44:28.527271",
+ "modified": "2020-04-29 05:20:41.629911",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Disbursement",
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 87e8a15..a5ed5de 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -106,6 +106,7 @@
def allocate_amounts(self, paid_entries):
self.set('repayment_details', [])
self.principal_amount_paid = 0
+ interest_paid = 0
if self.amount_paid - self.penalty_amount > 0 and paid_entries:
interest_paid = self.amount_paid - self.penalty_amount
@@ -286,7 +287,11 @@
pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid - against_loan_doc.total_interest_payable
if payment_type == "Loan Closure" and not payable_principal_amount:
- pending_days = date_diff(posting_date, entry.posting_date) + 1
+ if final_due_date:
+ pending_days = date_diff(posting_date, final_due_date)
+ else:
+ pending_days = date_diff(posting_date, against_loan_doc.disbursement_date) + 1
+
payable_principal_amount = pending_principal_amount
per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365
total_pending_interest += (pending_days * per_day_interest)
diff --git a/erpnext/loan_management/doctype/loan_security/loan_security.json b/erpnext/loan_management/doctype/loan_security/loan_security.json
index e6984ee..1d0bb30 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security.json
+++ b/erpnext/loan_management/doctype/loan_security/loan_security.json
@@ -1,23 +1,26 @@
{
- "autoname": "field:loan_security_name",
+ "actions": [],
+ "allow_rename": 1,
"creation": "2019-09-02 15:07:08.885593",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "loan_security_type",
- "loan_security_code",
"loan_security_name",
- "unit_of_measure",
- "column_break_3",
"haircut",
+ "loan_security_code",
+ "column_break_3",
+ "loan_security_type",
+ "unit_of_measure",
"disabled"
],
"fields": [
{
"fieldname": "loan_security_name",
"fieldtype": "Data",
+ "in_list_view": 1,
"label": "Loan Security Name",
+ "reqd": 1,
"unique": 1
},
{
@@ -33,8 +36,10 @@
{
"fieldname": "loan_security_type",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Loan Security Type",
- "options": "Loan Security Type"
+ "options": "Loan Security Type",
+ "reqd": 1
},
{
"fieldname": "loan_security_code",
@@ -52,11 +57,15 @@
"fetch_from": "loan_security_type.unit_of_measure",
"fieldname": "unit_of_measure",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Unit Of Measure",
- "options": "UOM"
+ "options": "UOM",
+ "read_only": 1,
+ "reqd": 1
}
],
- "modified": "2019-11-16 11:36:37.901656",
+ "links": [],
+ "modified": "2020-04-29 13:21:26.043492",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Security",
@@ -87,7 +96,6 @@
"write": 1
}
],
- "quick_entry": 1,
"search_fields": "loan_security_code",
"sort_field": "modified",
"sort_order": "DESC",
diff --git a/erpnext/loan_management/doctype/loan_security/loan_security.py b/erpnext/loan_management/doctype/loan_security/loan_security.py
index 800ad12..8858c81 100644
--- a/erpnext/loan_management/doctype/loan_security/loan_security.py
+++ b/erpnext/loan_management/doctype/loan_security/loan_security.py
@@ -7,4 +7,5 @@
from frappe.model.document import Document
class LoanSecurity(Document):
- pass
+ def autoname(self):
+ self.name = self.loan_security_name
diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json
index 5f29609..f46b88c 100644
--- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json
+++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json
@@ -9,9 +9,9 @@
"loan_security_type",
"unit_of_measure",
"haircut",
- "disabled",
"column_break_5",
- "loan_to_value_ratio"
+ "loan_to_value_ratio",
+ "disabled"
],
"fields": [
{
@@ -23,7 +23,9 @@
{
"fieldname": "loan_security_type",
"fieldtype": "Data",
+ "in_list_view": 1,
"label": "Loan Security Type",
+ "reqd": 1,
"unique": 1
},
{
@@ -34,8 +36,10 @@
{
"fieldname": "unit_of_measure",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Unit Of Measure",
- "options": "UOM"
+ "options": "UOM",
+ "reqd": 1
},
{
"fieldname": "column_break_5",
@@ -48,7 +52,7 @@
}
],
"links": [],
- "modified": "2020-02-28 12:43:20.364447",
+ "modified": "2020-04-28 14:06:49.046177",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Security Type",
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index f27197d..f93b244 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2017-12-01 12:12:55.048691",
"doctype": "DocType",
"editable_grid": 1,
@@ -6,8 +7,9 @@
"field_order": [
"item_code",
"item_name",
- "warehouse",
"material_request_type",
+ "from_warehouse",
+ "warehouse",
"column_break_4",
"quantity",
"uom",
@@ -46,6 +48,7 @@
{
"fieldname": "material_request_type",
"fieldtype": "Select",
+ "in_list_view": 1,
"label": "Material Request Type",
"options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
},
@@ -64,11 +67,11 @@
{
"fieldname": "projected_qty",
"fieldtype": "Float",
- "in_list_view": 1,
"label": "Projected Qty",
"read_only": 1
},
{
+ "default": "0",
"fieldname": "actual_qty",
"fieldtype": "Float",
"in_list_view": 1,
@@ -119,10 +122,18 @@
"label": "UOM",
"options": "UOM",
"read_only": 1
+ },
+ {
+ "depends_on": "eval:doc.material_request_type == 'Material Transfer'",
+ "fieldname": "from_warehouse",
+ "fieldtype": "Link",
+ "label": "From Warehouse",
+ "options": "Warehouse"
}
],
"istable": 1,
- "modified": "2019-11-08 15:15:43.979360",
+ "links": [],
+ "modified": "2020-02-03 12:22:29.913302",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index b49b0ba..64c952b 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -19,7 +19,8 @@
frm.set_query('for_warehouse', function(doc) {
return {
filters: {
- company: doc.company
+ company: doc.company,
+ is_group: 0
}
}
});
@@ -188,12 +189,53 @@
},
get_items_for_mr: function(frm) {
- const set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom',
+ if (!frm.doc.for_warehouse) {
+ frappe.throw(__("Select warehouse for material requests"));
+ }
+
+ if (frm.doc.ignore_existing_ordered_qty) {
+ frm.events.get_items_for_material_requests(frm);
+ } else {
+ const title = __("Transfer Materials For Warehouse {0}", [frm.doc.for_warehouse]);
+ var dialog = new frappe.ui.Dialog({
+ title: title,
+ fields: [
+ {
+ "fieldtype": "Table MultiSelect", "label": __("Source Warehouses"),
+ "fieldname": "warehouses", "options": "Production Plan Material Request Warehouse",
+ "description": "System will pickup the materials from the selected warehouses",
+ get_query: function () {
+ return {
+ filters: {
+ company: frm.doc.company
+ }
+ };
+ },
+ },
+ ]
+ });
+
+ dialog.show();
+
+ dialog.set_primary_action(__("Get Items"), () => {
+ let warehouses = dialog.get_values().warehouses;
+ frm.events.get_items_for_material_requests(frm, warehouses);
+ dialog.hide();
+ });
+ }
+ },
+
+ get_items_for_material_requests: function(frm, warehouses) {
+ const set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom', 'from_warehouse',
'min_order_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'material_request_type'];
+
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
freeze: true,
- args: {doc: frm.doc},
+ args: {
+ doc: frm.doc,
+ warehouses: warehouses || []
+ },
callback: function(r) {
if(r.message) {
frm.set_value('mr_items', []);
@@ -212,14 +254,14 @@
},
for_warehouse: function(frm) {
- if (frm.doc.mr_items) {
+ if (frm.doc.mr_items && frm.doc.for_warehouse) {
frm.trigger("get_items_for_mr");
}
},
download_materials_required: function(frm) {
let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
- open_url_post(frappe.request.url, { cmd: get_template_url, production_plan: frm.doc.name });
+ open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc });
},
show_progress: function(frm) {
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 77ca6b6..90e8b22 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -43,6 +43,7 @@
"total_produced_qty",
"column_break_32",
"status",
+ "warehouses",
"amended_from"
],
"fields": [
@@ -219,12 +220,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "for_warehouse",
- "fieldtype": "Link",
- "label": "For Warehouse",
- "options": "Warehouse"
- },
- {
"depends_on": "eval:!doc.__islocal",
"fieldname": "download_materials_required",
"fieldtype": "Button",
@@ -292,12 +287,26 @@
"options": "Production Plan",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "for_warehouse",
+ "fieldtype": "Link",
+ "label": "Material Request Warehouse",
+ "options": "Warehouse"
+ },
+ {
+ "fieldname": "warehouses",
+ "fieldtype": "Table MultiSelect",
+ "hidden": 1,
+ "label": "Warehouses",
+ "options": "Production Plan Material Request Warehouse",
+ "read_only": 1
}
],
"icon": "fa fa-calendar",
"is_submittable": 1,
"links": [],
- "modified": "2020-01-21 19:13:10.113854",
+ "modified": "2020-02-03 00:25:25.934202",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index c3f27cd..560286e 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -3,7 +3,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, json
+import frappe, json, copy
from frappe import msgprint, _
from six import string_types, iteritems
@@ -385,6 +385,7 @@
# add item
material_request.append("items", {
"item_code": item.item_code,
+ "from_warehouse": item.from_warehouse,
"qty": item.quantity,
"schedule_date": schedule_date,
"warehouse": item.warehouse,
@@ -415,19 +416,18 @@
msgprint(_("No material request created"))
@frappe.whitelist()
-def download_raw_materials(production_plan):
- doc = frappe.get_doc('Production Plan', production_plan)
- doc.check_permission()
+def download_raw_materials(doc):
+ if isinstance(doc, string_types):
+ doc = frappe._dict(json.loads(doc))
item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse',
'projected Qty', 'Actual Qty']]
- doc = doc.as_dict()
- for d in get_items_for_material_requests(doc, ignore_existing_ordered_qty=True):
+ for d in get_items_for_material_requests(doc):
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('quantity'),
d.get('warehouse'), d.get('projected_qty'), d.get('actual_qty')])
- if not doc.for_warehouse:
+ if not doc.get('for_warehouse'):
row = {'item_code': d.get('item_code')}
for bin_dict in get_bin_details(row, doc.company, all_warehouse=True):
if d.get("warehouse") == bin_dict.get('warehouse'):
@@ -610,26 +610,43 @@
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
@frappe.whitelist()
-def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
+def get_items_for_material_requests(doc, warehouses=None):
if isinstance(doc, string_types):
doc = frappe._dict(json.loads(doc))
+ warehouse_list = []
+ if warehouses:
+ if isinstance(warehouses, string_types):
+ warehouses = json.loads(warehouses)
+
+ for row in warehouses:
+ child_warehouses = frappe.db.get_descendants('Warehouse', row.get("warehouse"))
+ if child_warehouses:
+ warehouse_list.extend(child_warehouses)
+ else:
+ warehouse_list.append(row.get("warehouse"))
+
+ if warehouse_list:
+ warehouses = list(set(warehouse_list))
+
+ if doc.get("for_warehouse") and doc.get("for_warehouse") in warehouses:
+ warehouses.remove(doc.get("for_warehouse"))
+
+ warehouse_list = None
+
doc['mr_items'] = []
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
if not po_items:
frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
company = doc.get('company')
- warehouse = doc.get('for_warehouse')
-
- if not ignore_existing_ordered_qty:
- ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
+ ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
so_item_details = frappe._dict()
for data in po_items:
planned_qty = data.get('required_qty') or data.get('planned_qty')
ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
- warehouse = data.get("warehouse") or warehouse
+ warehouse = doc.get('for_warehouse')
item_details = {}
if data.get("bom") or data.get("bom_no"):
@@ -700,12 +717,51 @@
if items:
mr_items.append(items)
+ if not ignore_existing_ordered_qty and warehouses:
+ new_mr_items = []
+ for item in mr_items:
+ get_materials_from_other_locations(item, warehouses, new_mr_items, company)
+
+ mr_items = new_mr_items
+
if not mr_items:
- frappe.msgprint(_("""As raw materials projected quantity is more than required quantity, there is no need to create material request.
- Still if you want to make material request, kindly enable <b>Ignore Existing Projected Quantity</b> checkbox"""))
+ frappe.msgprint(_("""As raw materials projected quantity is more than required quantity,
+ there is no need to create material request for the warehouse {0}.
+ Still if you want to make material request,
+ kindly enable <b>Ignore Existing Projected Quantity</b> checkbox""").format(doc.get('for_warehouse')))
return mr_items
+def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
+ from erpnext.stock.doctype.pick_list.pick_list import get_available_item_locations
+ locations = get_available_item_locations(item.get("item_code"),
+ warehouses, item.get("quantity"), company, ignore_validation=True)
+
+ if not locations:
+ new_mr_items.append(item)
+ return
+
+ required_qty = item.get("quantity")
+ for d in locations:
+ if required_qty <=0: return
+
+ new_dict = copy.deepcopy(item)
+ quantity = required_qty if d.get("qty") > required_qty else d.get("qty")
+
+ if required_qty > 0:
+ new_dict.update({
+ "quantity": quantity,
+ "material_request_type": "Material Transfer",
+ "from_warehouse": d.get("warehouse")
+ })
+
+ required_qty -= quantity
+ new_mr_items.append(new_dict)
+
+ if required_qty:
+ item["quantity"] = required_qty
+ new_mr_items.append(item)
+
@frappe.whitelist()
def get_item_data(item_code):
item_details = get_item_details(item_code)
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 26f580d..ca67d71 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -62,9 +62,9 @@
def test_production_plan_for_existing_ordered_qty(self):
sr1 = create_stock_reconciliation(item_code="Raw Material Item 1",
- target="_Test Warehouse - _TC", qty=1, rate=100)
+ target="_Test Warehouse - _TC", qty=1, rate=110)
sr2 = create_stock_reconciliation(item_code="Raw Material Item 2",
- target="_Test Warehouse - _TC", qty=1, rate=100)
+ target="_Test Warehouse - _TC", qty=1, rate=120)
pln = create_production_plan(item_code='Test Production Item 1', ignore_existing_ordered_qty=0)
self.assertTrue(len(pln.mr_items), 1)
@@ -86,9 +86,9 @@
def test_production_plan_without_multi_level_for_existing_ordered_qty(self):
sr1 = create_stock_reconciliation(item_code="Raw Material Item 1",
- target="_Test Warehouse - _TC", qty=1, rate=100)
+ target="_Test Warehouse - _TC", qty=1, rate=130)
sr2 = create_stock_reconciliation(item_code="Subassembly Item 1",
- target="_Test Warehouse - _TC", qty=1, rate=100)
+ target="_Test Warehouse - _TC", qty=1, rate=140)
pln = create_production_plan(item_code='Test Production Item 1',
use_multi_level_bom=0, ignore_existing_ordered_qty=0)
diff --git a/erpnext/education/doctype/video/__init__.py b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/__init__.py
similarity index 100%
copy from erpnext/education/doctype/video/__init__.py
copy to erpnext/manufacturing/doctype/production_plan_material_request_warehouse/__init__.py
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js
new file mode 100644
index 0000000..53f8758
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Production Plan Material Request Warehouse', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
new file mode 100644
index 0000000..53e33c0
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
@@ -0,0 +1,42 @@
+{
+ "actions": [],
+ "creation": "2020-02-02 10:37:16.650836",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "warehouse"
+ ],
+ "fields": [
+ {
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "label": "Warehouse",
+ "options": "Warehouse"
+ }
+ ],
+ "links": [],
+ "modified": "2020-02-02 10:37:16.650836",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Production Plan Material Request Warehouse",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py
new file mode 100644
index 0000000..f605985
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.py
@@ -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 ProductionPlanMaterialRequestWarehouse(Document):
+ pass
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
new file mode 100644
index 0000000..ecab5fb
--- /dev/null
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/test_production_plan_material_request_warehouse.py
@@ -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 TestProductionPlanMaterialRequestWarehouse(unittest.TestCase):
+ pass
diff --git a/erpnext/non_profit/doctype/member/member_dashboard.py b/erpnext/non_profit/doctype/member/member_dashboard.py
index 945fb7b..743db25 100644
--- a/erpnext/non_profit/doctype/member/member_dashboard.py
+++ b/erpnext/non_profit/doctype/member/member_dashboard.py
@@ -6,10 +6,17 @@
'heatmap': True,
'heatmap_message': _('Member Activity'),
'fieldname': 'member',
+ 'non_standard_fieldnames': {
+ 'Bank Account': 'party'
+ },
'transactions': [
{
'label': _('Membership Details'),
'items': ['Membership']
+ },
+ {
+ 'label': _('Fee'),
+ 'items': ['Bank Account']
}
]
- }
\ No newline at end of file
+ }
diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py
index a523a23..5a69cdb 100644
--- a/erpnext/non_profit/doctype/membership/membership.py
+++ b/erpnext/non_profit/doctype/membership/membership.py
@@ -62,11 +62,9 @@
'subscription_id': subscription_id,
'email_id': email
}, order_by="creation desc")
-
return frappe.get_doc("Member", members[0]['name'])
-
-@frappe.whitelist()
+@frappe.whitelist(allow_guest=True)
def trigger_razorpay_subscription(data):
if isinstance(data, six.string_types):
data = json.loads(data)
@@ -88,10 +86,6 @@
if data.event == "subscription.activated":
member.customer_id = payment.customer_id
- member.subscription_start = datetime.fromtimestamp(subscription.start_at)
- member.subscription_end = datetime.fromtimestamp(subscription.end_at)
- member.subscription_activated = 1
- member.save(ignore_permissions=True)
elif data.event == "subscription.charged":
membership = frappe.new_doc("Membership")
membership.update({
@@ -108,6 +102,12 @@
})
membership.insert(ignore_permissions=True)
+ # Update these values anyway
+ member.subscription_start = datetime.fromtimestamp(subscription.start_at)
+ member.subscription_end = datetime.fromtimestamp(subscription.end_at)
+ member.subscription_activated = 1
+ member.save(ignore_permissions=True)
+
return True
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index a216f53..783fee1 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -1,6 +1,7 @@
execute:import unidecode # new requirement
erpnext.patches.v8_0.move_perpetual_inventory_setting
erpnext.patches.v8_9.set_print_zero_amount_taxes
+erpnext.patches.v12_0.update_is_cancelled_field
erpnext.patches.v11_0.rename_production_order_to_work_order
erpnext.patches.v11_0.refactor_naming_series
erpnext.patches.v11_0.refactor_autoname_naming
@@ -261,7 +262,6 @@
erpnext.patches.v6_21.fix_reorder_level
erpnext.patches.v6_21.rename_material_request_fields
erpnext.patches.v6_23.update_stopped_status_to_closed
-erpnext.patches.v6_24.repost_valuation_rate_for_serialized_items
erpnext.patches.v6_24.set_recurring_id
erpnext.patches.v6_20x.set_compact_print
execute:frappe.delete_doc_if_exists("Web Form", "contact") #2016-03-10
@@ -315,7 +315,6 @@
erpnext.patches.v7_0.rename_examination_to_assessment
erpnext.patches.v7_0.set_portal_settings
erpnext.patches.v7_0.update_change_amount_account
-erpnext.patches.v7_0.repost_future_gle_for_purchase_invoice
erpnext.patches.v7_0.fix_duplicate_icons
erpnext.patches.v7_0.repost_gle_for_pos_sales_return
erpnext.patches.v7_1.update_total_billing_hours
@@ -660,6 +659,7 @@
erpnext.patches.v12_0.set_job_offer_applicant_email
erpnext.patches.v12_0.create_irs_1099_field_united_states
erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
+erpnext.patches.v12_0.rename_bank_reconciliation
erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
erpnext.patches.v12_0.add_permission_in_lower_deduction
erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
@@ -674,4 +674,5 @@
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
erpnext.patches.v12_0.fix_quotation_expired_status
-erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
\ No newline at end of file
+erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
+erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
diff --git a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py b/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
index 68c06ef..e6546e3 100644
--- a/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
+++ b/erpnext/patches/v10_0/repost_gle_for_purchase_receipts_with_rejected_items.py
@@ -24,9 +24,9 @@
doc = frappe.get_doc("Purchase Receipt", d.name)
doc.docstatus = 2
- doc.make_gl_entries_on_cancel(repost_future_gle=False)
+ doc.make_gl_entries_on_cancel()
# update gl entries for submit state of PR
doc.docstatus = 1
- doc.make_gl_entries(repost_future_gle=False)
+ doc.make_gl_entries()
diff --git a/erpnext/patches/v10_0/taxes_issue_with_pos.py b/erpnext/patches/v10_0/taxes_issue_with_pos.py
index 9b54297..2a3275a 100644
--- a/erpnext/patches/v10_0/taxes_issue_with_pos.py
+++ b/erpnext/patches/v10_0/taxes_issue_with_pos.py
@@ -19,7 +19,7 @@
doc.db_update()
delete_gle_for_voucher(doc.name)
- doc.make_gl_entries(repost_future_gle=False)
+ doc.make_gl_entries()
def delete_gle_for_voucher(voucher_no):
frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""",
diff --git a/erpnext/patches/v12_0/add_default_dashboards.py b/erpnext/patches/v12_0/add_default_dashboards.py
index ab92fba..0c3f2f8 100644
--- a/erpnext/patches/v12_0/add_default_dashboards.py
+++ b/erpnext/patches/v12_0/add_default_dashboards.py
@@ -5,4 +5,5 @@
from erpnext.setup.setup_wizard.operations.install_fixtures import add_dashboards
def execute():
+ frappe.reload_doc("desk", "doctype", "number_card_link")
add_dashboards()
diff --git a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py
index 3e4c87f..82c8f5c 100644
--- a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py
+++ b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py
@@ -3,8 +3,11 @@
from erpnext.regional.united_states.setup import make_custom_fields
def execute():
- company = frappe.get_all('Company', filters = {'country': 'United States'})
- if not company:
- return
- make_custom_fields()
\ No newline at end of file
+ frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True)
+
+ company = frappe.get_all('Company', filters = {'country': 'United States'})
+ if not company:
+ return
+
+ make_custom_fields()
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation.py b/erpnext/patches/v12_0/rename_bank_reconciliation.py
new file mode 100644
index 0000000..eda47a9
--- /dev/null
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2018, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ if frappe.db.table_exists("Bank Reconciliation"):
+ frappe.rename_doc('DocType', 'Bank Reconciliation', 'Bank Clearance', force=True)
+ frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance')
+
+ frappe.rename_doc('DocType', 'Bank Reconciliation Detail', 'Bank Clearance Detail', force=True)
+ frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance Detail')
+
+ frappe.delete_doc("DocType", "Bank Reconciliation")
+ frappe.delete_doc("DocType", "Bank Reconciliation Detail")
diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
index caeda8a..978b1c9 100644
--- a/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
+++ b/erpnext/patches/v12_0/rename_bank_reconciliation_fields.py
@@ -9,6 +9,6 @@
frappe.db.sql("UPDATE tabSingles SET field='{new_name}' WHERE doctype='{doctype}' AND field='{old_name}';".format(**kwargs)) #nosec
def execute():
- _rename_single_field(doctype = "Bank Reconciliation", old_name = "bank_account" , new_name = "account")
- _rename_single_field(doctype = "Bank Reconciliation", old_name = "bank_account_no", new_name = "bank_account")
- frappe.reload_doc("Accounts", "doctype", "Bank Reconciliation")
+ _rename_single_field(doctype = "Bank Clearance", old_name = "bank_account" , new_name = "account")
+ _rename_single_field(doctype = "Bank Clearance", old_name = "bank_account_no", new_name = "bank_account")
+ frappe.reload_doc("Accounts", "doctype", "Bank Clearance")
diff --git a/erpnext/patches/v12_0/retain_permission_rules_for_video_doctype.py b/erpnext/patches/v12_0/retain_permission_rules_for_video_doctype.py
new file mode 100644
index 0000000..ca8a13b
--- /dev/null
+++ b/erpnext/patches/v12_0/retain_permission_rules_for_video_doctype.py
@@ -0,0 +1,21 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ # to retain the roles and permissions from Education Module
+ # after moving doctype to core
+ permissions = frappe.db.sql("""
+ SELECT
+ *
+ FROM
+ `tabDocPerm`
+ WHERE
+ parent='Video'
+ """, as_dict=True)
+
+ frappe.reload_doc('core', 'doctype', 'video')
+ doc = frappe.get_doc('DocType', 'Video')
+ doc.permissions = []
+ for perm in permissions:
+ doc.append('permissions', perm)
+ doc.save()
diff --git a/erpnext/patches/v12_0/set_total_batch_quantity.py b/erpnext/patches/v12_0/set_total_batch_quantity.py
index d373275..7296eaa 100644
--- a/erpnext/patches/v12_0/set_total_batch_quantity.py
+++ b/erpnext/patches/v12_0/set_total_batch_quantity.py
@@ -6,6 +6,6 @@
for batch in frappe.get_all("Batch", fields=["name", "batch_id"]):
batch_qty = frappe.db.get_value("Stock Ledger Entry",
- {"docstatus": 1, "batch_no": batch.batch_id, "is_cancelled": "No"},
+ {"docstatus": 1, "batch_no": batch.batch_id, "is_cancelled": 0},
"sum(actual_qty)") or 0.0
frappe.db.set_value("Batch", batch.name, "batch_qty", batch_qty, update_modified=False)
diff --git a/erpnext/patches/v12_0/update_is_cancelled_field.py b/erpnext/patches/v12_0/update_is_cancelled_field.py
new file mode 100644
index 0000000..0b2e827
--- /dev/null
+++ b/erpnext/patches/v12_0/update_is_cancelled_field.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ try:
+ frappe.db.sql("UPDATE `tabStock Ledger Entry` SET is_cancelled = 0 where is_cancelled in ('', NULL, 'No')")
+ frappe.db.sql("UPDATE `tabSerial No` SET is_cancelled = 0 where is_cancelled in ('', NULL, 'No')")
+
+ frappe.db.sql("UPDATE `tabStock Ledger Entry` SET is_cancelled = 1 where is_cancelled = 'Yes'")
+ frappe.db.sql("UPDATE `tabSerial No` SET is_cancelled = 1 where is_cancelled = 'Yes'")
+
+ frappe.reload_doc("stock", "doctype", "stock_ledger_entry")
+ frappe.reload_doc("stock", "doctype", "serial_no")
+ except:
+ pass
\ No newline at end of file
diff --git a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py b/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py
index 16932af..c6c94d4 100644
--- a/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py
+++ b/erpnext/patches/v4_2/fix_gl_entries_for_stock_transactions.py
@@ -8,7 +8,7 @@
def execute():
from erpnext.stock.stock_balance import repost
repost(allow_zero_rate=True, only_actual=True)
-
+
frappe.reload_doctype("Account")
warehouse_account = frappe.db.sql("""select name, master_name from tabAccount
@@ -43,7 +43,7 @@
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
voucher = frappe.get_doc(voucher_type, voucher_no)
- voucher.make_gl_entries(repost_future_gle=False)
+ voucher.make_gl_entries()
frappe.db.commit()
except Exception as e:
print(frappe.get_traceback())
diff --git a/erpnext/patches/v6_24/repost_valuation_rate_for_serialized_items.py b/erpnext/patches/v6_24/repost_valuation_rate_for_serialized_items.py
deleted file mode 100644
index 3b157a3..0000000
--- a/erpnext/patches/v6_24/repost_valuation_rate_for_serialized_items.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import today
-from erpnext.accounts.utils import get_fiscal_year
-from erpnext.stock.stock_ledger import update_entries_after
-
-def execute():
- try:
- year_start_date = get_fiscal_year(today())[1]
- except:
- return
-
- if year_start_date:
- items = frappe.db.sql("""select distinct item_code, warehouse from `tabStock Ledger Entry`
- where ifnull(serial_no, '') != '' and actual_qty > 0 and incoming_rate=0""", as_dict=1)
-
- for d in items:
- try:
- update_entries_after({
- "item_code": d.item_code,
- "warehouse": d.warehouse,
- "posting_date": year_start_date
- }, allow_zero_rate=True)
- except:
- pass
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py b/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py
deleted file mode 100644
index 9e21fb6..0000000
--- a/erpnext/patches/v7_0/repost_future_gle_for_purchase_invoice.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import cint
-from erpnext.stock import get_warehouse_account_map
-from erpnext.controllers.stock_controller import update_gl_entries_after
-
-def execute():
- company_list = frappe.db.sql_list("""Select name from tabCompany where enable_perpetual_inventory = 1""")
- frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
-
- frappe.reload_doctype("Purchase Invoice")
- wh_account = get_warehouse_account_map()
-
- for pi in frappe.get_all("Purchase Invoice", fields=["name", "company"], filters={"docstatus": 1, "update_stock": 1}):
- if pi.company in company_list:
- pi_doc = frappe.get_doc("Purchase Invoice", pi.name)
- items, warehouses = pi_doc.get_items_and_warehouses()
- update_gl_entries_after(pi_doc.posting_date, pi_doc.posting_time,
- warehouses, items, wh_account, company = pi.company)
-
- frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py b/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py
index 2d1a151..b864e59 100644
--- a/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py
+++ b/erpnext/patches/v7_0/repost_gle_for_pi_with_update_stock.py
@@ -8,13 +8,13 @@
def execute():
frappe.reload_doctype("Purchase Invoice")
- for pi in frappe.db.sql("""select name from `tabPurchase Invoice`
- where company in(select name from tabCompany where enable_perpetual_inventory = 1) and
+ for pi in frappe.db.sql("""select name from `tabPurchase Invoice`
+ where company in(select name from tabCompany where enable_perpetual_inventory = 1) and
update_stock=1 and docstatus=1 order by posting_date asc""", as_dict=1):
-
- frappe.db.sql("""delete from `tabGL Entry`
+
+ frappe.db.sql("""delete from `tabGL Entry`
where voucher_type = 'Purchase Invoice' and voucher_no = %s""", pi.name)
-
+
pi_doc = frappe.get_doc("Purchase Invoice", pi.name)
- pi_doc.make_gl_entries(repost_future_gle=False)
+ pi_doc.make_gl_entries()
frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project_template/project_template.json b/erpnext/projects/doctype/project_template/project_template.json
index 8352995..445ad9f 100644
--- a/erpnext/projects/doctype/project_template/project_template.json
+++ b/erpnext/projects/doctype/project_template/project_template.json
@@ -1,130 +1,52 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
+ "actions": [],
"autoname": "Prompt",
- "beta": 0,
"creation": "2019-02-18 17:23:11.708371",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "project_type",
+ "tasks"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "project_type",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
"in_list_view": 1,
- "in_standard_filter": 0,
"label": "Project Type",
- "length": 0,
- "no_copy": 0,
- "options": "Project Type",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "options": "Project Type"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "tasks",
"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": "Tasks",
- "length": 0,
- "no_copy": 0,
"options": "Project Template Task",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "reqd": 1
}
],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-02-18 18:01:26.519832",
+ "links": [],
+ "modified": "2020-04-26 02:23:53.990322",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Template",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index 1c12c35..2ce49e7 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -50,7 +50,7 @@
show_stock_ledger: function() {
var me = this;
- if(this.frm.doc.docstatus===1) {
+ if(this.frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__("Stock Ledger"), function() {
frappe.route_options = {
voucher_no: me.frm.doc.name,
@@ -66,7 +66,7 @@
show_general_ledger: function() {
var me = this;
- if(this.frm.doc.docstatus===1) {
+ if(this.frm.doc.docstatus > 0) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: me.frm.doc.name,
diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js
index 75c5a82..b223fc5 100644
--- a/erpnext/public/js/utils/dimension_tree_filter.js
+++ b/erpnext/public/js/utils/dimension_tree_filter.js
@@ -24,7 +24,7 @@
onload: function(frm) {
erpnext.dimension_filters.forEach((dimension) => {
frappe.model.with_doctype(dimension['document_type'], () => {
- if (frappe.meta.has_field(dimension['document_type'], 'is_group')) {
+ if(frappe.meta.has_field(dimension['document_type'], 'is_group')) {
frm.set_query(dimension['fieldname'], {
"is_group": 0
});
@@ -42,19 +42,21 @@
update_dimension: function(frm) {
erpnext.dimension_filters.forEach((dimension) => {
- if (frm.is_new()) {
- if (frm.doc.company && Object.keys(default_dimensions || {}).length > 0
+ if(frm.is_new()) {
+ if(frm.doc.company && Object.keys(default_dimensions || {}).length > 0
&& default_dimensions[frm.doc.company]) {
- if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
- frm.set_value(dimension['fieldname'],
- default_dimensions[frm.doc.company][dimension['document_type']]);
- }
+ let default_dimension = default_dimensions[frm.doc.company][dimension['document_type']];
- $.each(frm.doc.items || frm.doc.accounts || [], function(i, row) {
- frappe.model.set_value(row.doctype, row.name, dimension['fieldname'],
- default_dimensions[frm.doc.company][dimension['document_type']])
- });
+ if(default_dimension) {
+ if (frappe.meta.has_field(doctype, dimension['fieldname'])) {
+ frm.set_value(dimension['fieldname'], default_dimension);
+ }
+
+ $.each(frm.doc.items || frm.doc.accounts || [], function(i, row) {
+ frappe.model.set_value(row.doctype, row.name, dimension['fieldname'], default_dimension);
+ });
+ }
}
}
});
@@ -76,20 +78,6 @@
var row = frappe.get_doc(cdt, cdn);
frm.script_manager.copy_from_first_row("accounts", row, [dimension['fieldname']]);
});
- },
-
- items_add: function(frm, cdt, cdn) {
- erpnext.dimension_filters.forEach((dimension) => {
- var row = frappe.get_doc(cdt, cdn);
- frm.script_manager.copy_from_first_row("items", row, [dimension['fieldname']]);
- });
- },
-
- accounts_add: function(frm, cdt, cdn) {
- erpnext.dimension_filters.forEach((dimension) => {
- var row = frappe.get_doc(cdt, cdn);
- frm.script_manager.copy_from_first_row("accounts", row, [dimension['fieldname']]);
- });
}
});
});
\ No newline at end of file
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
index 6860ed3..39486df 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.json
@@ -28,7 +28,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Client ID",
- "reqd": 1
+ "reqd": 1,
+ "length": 5
},
{
"fieldname": "consultant",
@@ -42,7 +43,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Consultant ID",
- "reqd": 1
+ "reqd": 1,
+ "length": 7
},
{
"fieldname": "column_break_2",
@@ -102,4 +104,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 094f010..3309858 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -288,7 +288,7 @@
})
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
- salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1)
+ salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1, ignore_permissions=True)
basic_amt, hra_amt = 0, 0
for earning in salary_slip.earnings:
if earning.salary_component == basic_component:
diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py
index 654dd48..22e30e3 100644
--- a/erpnext/selling/doctype/customer/customer_dashboard.py
+++ b/erpnext/selling/doctype/customer/customer_dashboard.py
@@ -11,7 +11,8 @@
'non_standard_fieldnames': {
'Payment Entry': 'party',
'Quotation': 'party_name',
- 'Opportunity': 'party_name'
+ 'Opportunity': 'party_name',
+ 'Bank Account': 'party'
},
'dynamic_links': {
'party_name': ['Customer', 'quotation_to']
@@ -27,7 +28,7 @@
},
{
'label': _('Payments'),
- 'items': ['Payment Entry']
+ 'items': ['Payment Entry', 'Bank Account']
},
{
'label': _('Support'),
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index d8e9a63..b8b0d40 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -335,7 +335,7 @@
self.assertEqual(so.get("items")[-1].qty, 7)
self.assertEqual(so.get("items")[-1].amount, 1400)
self.assertEqual(so.status, 'To Deliver and Bill')
-
+
def test_remove_item_in_update_child_qty_rate(self):
so = make_sales_order(**{
"item_list": [{
@@ -373,7 +373,7 @@
"docname": so.get("items")[0].name
}])
update_child_qty_rate('Sales Order', trans_item, so.name)
-
+
so.reload()
self.assertEqual(len(so.get("items")), 1)
self.assertEqual(so.status, 'To Deliver and Bill')
@@ -760,10 +760,9 @@
self.assertEqual(reserved_serial_no, dn.get("items")[0].serial_no)
item_line = dn.get("items")[0]
item_line.serial_no = item_serial_no.name
- self.assertRaises(frappe.ValidationError, dn.submit)
item_line = dn.get("items")[0]
item_line.serial_no = reserved_serial_no
- self.assertTrue(dn.submit)
+ dn.submit()
dn.load_from_db()
dn.cancel()
si = make_sales_invoice(so.name)
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
index 0cb606b..857b982 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
@@ -11,8 +11,8 @@
def get_data_column(filters, partner_doctype):
data = []
- period_list = get_period_list(filters.fiscal_year, filters.fiscal_year,
- filters.period, company=filters.company)
+ period_list = get_period_list(filters.fiscal_year, filters.fiscal_year, '', '',
+ 'Fiscal Year', filters.period, company=filters.company)
rows = get_data(filters, period_list, partner_doctype)
columns = get_columns(filters, period_list, partner_doctype)
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index e4986e3..3be6f44 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -32,7 +32,7 @@
{ 'doctype': 'Domain', 'domain': 'Agriculture'},
{ 'doctype': 'Domain', 'domain': 'Non Profit'},
- # ensure at least an empty Address Template exists for this Country
+ # ensure at least an empty Address Template exists for this Country
{'doctype':"Address Template", "country": country},
# item group
@@ -271,7 +271,7 @@
# Records for the Supplier Scorecard
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
-
+
make_default_records()
make_records(records)
set_up_address_templates(default_country=country)
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 73b36e3..7acdec7 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -23,22 +23,19 @@
if not args.get("posting_date"):
args["posting_date"] = nowdate()
- # update valuation and qty after transaction for post dated entry
- if args.get("is_cancelled") == "Yes" and via_landed_cost_voucher:
- return
update_entries_after({
"item_code": self.item_code,
"warehouse": self.warehouse,
"posting_date": args.get("posting_date"),
"posting_time": args.get("posting_time"),
- "voucher_no": args.get("voucher_no")
+ "voucher_no": args.get("voucher_no"),
+ "sle_id": args.sle_id
}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
def update_qty(self, args):
# update the stock values (for current quantities)
if args.get("voucher_type")=="Stock Reconciliation":
- if args.get('is_cancelled') == 'No':
- self.actual_qty = args.get("qty_after_transaction")
+ self.actual_qty = args.get("qty_after_transaction")
else:
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index f8608d8..68836b4 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -188,7 +188,7 @@
}
}
- if (doc.docstatus==1) {
+ if (doc.docstatus > 0) {
this.show_stock_ledger();
if (erpnext.is_perpetual_inventory_enabled(doc.company)) {
this.show_general_ledger();
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index dc96e7b..37f9097 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -222,6 +222,7 @@
self.cancel_packing_slips()
self.make_gl_entries_on_cancel()
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index d7a93fb..bf7007a 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -61,54 +61,55 @@
self.assertFalse(get_gl_entries("Delivery Note", dn.name))
- def test_delivery_note_gl_entry(self):
- company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
+ # def test_delivery_note_gl_entry(self):
+ # company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
- set_valuation_method("_Test Item", "FIFO")
+ # set_valuation_method("_Test Item", "FIFO")
- make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
+ # make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
- stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
- prev_bal = get_balance_on(stock_in_hand_account)
+ # stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
+ # prev_bal = get_balance_on(stock_in_hand_account)
- dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
+ # dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
- gl_entries = get_gl_entries("Delivery Note", dn.name)
- self.assertTrue(gl_entries)
+ # gl_entries = get_gl_entries("Delivery Note", dn.name)
+ # self.assertTrue(gl_entries)
- stock_value_difference = abs(frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Delivery Note", "voucher_no": dn.name}, "stock_value_difference"))
+ # stock_value_difference = abs(frappe.db.get_value("Stock Ledger Entry",
+ # {"voucher_type": "Delivery Note", "voucher_no": dn.name}, "stock_value_difference"))
- expected_values = {
- stock_in_hand_account: [0.0, stock_value_difference],
- "Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
- }
- for i, gle in enumerate(gl_entries):
- self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
+ # expected_values = {
+ # stock_in_hand_account: [0.0, stock_value_difference],
+ # "Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
+ # }
+ # for i, gle in enumerate(gl_entries):
+ # self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
- # check stock in hand balance
- bal = get_balance_on(stock_in_hand_account)
- self.assertEqual(bal, prev_bal - stock_value_difference)
+ # # check stock in hand balance
+ # bal = get_balance_on(stock_in_hand_account)
+ # self.assertEqual(bal, prev_bal - stock_value_difference)
- # back dated incoming entry
- make_stock_entry(posting_date=add_days(nowdate(), -2), target="Stores - TCP1",
- qty=5, basic_rate=100)
+ # # back dated incoming entry
+ # make_stock_entry(posting_date=add_days(nowdate(), -2), target="Stores - TCP1",
+ # qty=5, basic_rate=100)
- gl_entries = get_gl_entries("Delivery Note", dn.name)
- self.assertTrue(gl_entries)
+ # gl_entries = get_gl_entries("Delivery Note", dn.name)
+ # self.assertTrue(gl_entries)
- stock_value_difference = abs(frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Delivery Note", "voucher_no": dn.name}, "stock_value_difference"))
+ # stock_value_difference = abs(frappe.db.get_value("Stock Ledger Entry",
+ # {"voucher_type": "Delivery Note", "voucher_no": dn.name}, "stock_value_difference"))
- expected_values = {
- stock_in_hand_account: [0.0, stock_value_difference],
- "Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
- }
- for i, gle in enumerate(gl_entries):
- self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
+ # expected_values = {
+ # stock_in_hand_account: [0.0, stock_value_difference],
+ # "Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
+ # }
+ # for i, gle in enumerate(gl_entries):
+ # self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
- dn.cancel()
- self.assertFalse(get_gl_entries("Delivery Note", dn.name))
+ # dn.cancel()
+ # self.assertTrue(get_gl_entries("Delivery Note", dn.name))
+ # set_perpetual_inventory(0, company)
def test_delivery_note_gl_entry_packing_item(self):
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
@@ -147,7 +148,6 @@
self.assertEqual(flt(bal, 2), flt(prev_bal - stock_value_diff, 2))
dn.cancel()
- self.assertFalse(get_gl_entries("Delivery Note", dn.name))
def test_serialized(self):
se = make_serialized_item()
@@ -464,27 +464,19 @@
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
dn1 = make_delivery_note(so.name)
- dn1.set_posting_time = 1
- dn1.posting_time = "10:00"
dn1.get("items")[0].qty = 2
dn1.submit()
+ dn2 = make_delivery_note(so.name)
+ dn2.get("items")[0].qty = 3
+ dn2.submit()
+
+ dn1.load_from_db()
self.assertEqual(dn1.get("items")[0].billed_amt, 200)
self.assertEqual(dn1.per_billed, 100)
self.assertEqual(dn1.status, "Completed")
- dn2 = make_delivery_note(so.name)
- dn2.set_posting_time = 1
- dn2.posting_time = "08:00"
- dn2.get("items")[0].qty = 4
- dn2.submit()
-
- dn1.load_from_db()
- self.assertEqual(dn1.get("items")[0].billed_amt, 100)
- self.assertEqual(dn1.per_billed, 50)
- self.assertEqual(dn1.status, "To Bill")
-
- self.assertEqual(dn2.get("items")[0].billed_amt, 400)
+ self.assertEqual(dn2.get("items")[0].billed_amt, 300)
self.assertEqual(dn2.per_billed, 100)
self.assertEqual(dn2.status, "Completed")
@@ -497,8 +489,6 @@
so = make_sales_order()
dn1 = make_delivery_note(so.name)
- dn1.set_posting_time = 1
- dn1.posting_time = "10:00"
dn1.get("items")[0].qty = 2
dn1.submit()
@@ -513,7 +503,6 @@
si2.submit()
dn2 = make_delivery_note(so.name)
- dn2.posting_time = "08:00"
dn2.get("items")[0].qty = 5
dn2.submit()
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 7d2e311..c371999 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -1060,7 +1060,7 @@
"image_field": "image",
"links": [],
"max_attachments": 1,
- "modified": "2020-04-07 15:56:06.195722",
+ "modified": "2020-04-08 15:56:06.195722",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",
@@ -1122,4 +1122,4 @@
"sort_order": "DESC",
"title_field": "item_name",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 5ad0e13..bc3d326 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -137,7 +137,7 @@
# update stock & gl entries for cancelled state of PR
doc.docstatus = 2
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
- doc.make_gl_entries_on_cancel(repost_future_gle=False)
+ doc.make_gl_entries_on_cancel()
# update stock & gl entries for submit state of PR
doc.docstatus = 1
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 62d369c..3f2c5da 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -15,8 +15,9 @@
def test_landed_cost_voucher(self):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True)
-
+ pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1",
+ get_multiple_items = True, get_taxes_and_charges = True)
last_sle = frappe.db.get_value("Stock Ledger Entry", {
"voucher_type": pr.doctype,
@@ -26,7 +27,7 @@
},
fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
- submit_landed_cost_voucher("Purchase Receipt", pr.name)
+ submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
self.assertEqual(pr_lc_value, 25.0)
@@ -67,8 +68,9 @@
}
for gle in gl_entries:
- self.assertEqual(expected_values[gle.account][0], gle.debit)
- self.assertEqual(expected_values[gle.account][1], gle.credit)
+ if not gle.get('is_cancelled'):
+ self.assertEqual(expected_values[gle.account][0], gle.debit)
+ self.assertEqual(expected_values[gle.account][1], gle.credit)
def test_landed_cost_voucher_against_purchase_invoice(self):
@@ -87,7 +89,7 @@
},
fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
- submit_landed_cost_voucher("Purchase Invoice", pi.name)
+ submit_landed_cost_voucher("Purchase Invoice", pi.name, pi.company)
pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name},
"landed_cost_voucher_amount")
@@ -118,8 +120,9 @@
}
for gle in gl_entries:
- self.assertEqual(expected_values[gle.account][0], gle.debit)
- self.assertEqual(expected_values[gle.account][1], gle.credit)
+ if not gle.get('is_cancelled'):
+ self.assertEqual(expected_values[gle.account][0], gle.debit)
+ self.assertEqual(expected_values[gle.account][1], gle.credit)
def test_landed_cost_voucher_for_serialized_item(self):
@@ -134,7 +137,7 @@
serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate")
- submit_landed_cost_voucher("Purchase Receipt", pr.name)
+ submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company)
serial_no = frappe.db.get_value("Serial No", "SN001",
["warehouse", "purchase_rate"], as_dict=1)
@@ -157,13 +160,13 @@
})
pr.submit()
- lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, 123.22)
+ lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
self.assertEqual(lcv.items[0].applicable_charges, 41.07)
self.assertEqual(lcv.items[2].applicable_charges, 41.08)
def test_multiple_landed_cost_voucher_against_pr(self):
- pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
+ pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
supplier_warehouse = "Stores - TCP1", do_not_save=True)
pr.append("items", {
@@ -176,7 +179,7 @@
pr.submit()
- lcv1 = make_landed_cost_voucher(receipt_document_type = 'Purchase Receipt',
+ lcv1 = make_landed_cost_voucher(company = pr.company, receipt_document_type = 'Purchase Receipt',
receipt_document=pr.name, charges=100, do_not_save=True)
lcv1.insert()
@@ -187,7 +190,7 @@
lcv1.submit()
- lcv2 = make_landed_cost_voucher(receipt_document_type = 'Purchase Receipt',
+ lcv2 = make_landed_cost_voucher(company = pr.company, receipt_document_type = 'Purchase Receipt',
receipt_document=pr.name, charges=100, do_not_save=True)
lcv2.insert()
@@ -208,7 +211,7 @@
ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)
lcv = frappe.new_doc('Landed Cost Voucher')
- lcv.company = '_Test Company'
+ lcv.company = args.company or '_Test Company'
lcv.distribute_charges_based_on = 'Amount'
lcv.set('purchase_receipts', [{
@@ -233,11 +236,11 @@
return lcv
-def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=50):
+def submit_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50):
ref_doc = frappe.get_doc(receipt_document_type, receipt_document)
lcv = frappe.new_doc("Landed Cost Voucher")
- lcv.company = "_Test Company"
+ lcv.company = company
lcv.distribute_charges_based_on = 'Amount'
lcv.set("purchase_receipts", [{
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index eb298a6..db8bffd 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -20,6 +20,17 @@
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; });
+ frm.set_query("item_code", "items", function() {
+ return {
+ query: "erpnext.controllers.queries.item_query"
+ };
+ });
+
+ frm.set_query("from_warehouse", "items", function(doc) {
+ return {
+ filters: {'company': doc.company}
+ };
+ })
},
onload: function(frm) {
@@ -53,6 +64,16 @@
frm.toggle_reqd('customer', frm.doc.material_request_type=="Customer Provided");
},
+ set_from_warehouse: function(frm) {
+ if (frm.doc.material_request_type == "Material Transfer"
+ && frm.doc.set_from_warehouse) {
+ frm.doc.items.forEach(d => {
+ frappe.model.set_value(d.doctype, d.name,
+ "from_warehouse", frm.doc.set_from_warehouse);
+ })
+ }
+ },
+
make_custom_buttons: function(frm) {
if (frm.doc.docstatus==0) {
frm.add_custom_button(__("Bill of Materials"),
@@ -159,6 +180,7 @@
args: {
args: {
item_code: item.item_code,
+ from_warehouse: item.from_warehouse,
warehouse: item.warehouse,
doctype: frm.doc.doctype,
buying_price_list: frappe.defaults.get_default('buying_price_list'),
@@ -176,9 +198,11 @@
},
callback: function(r) {
const d = item;
+ const qty_fields = ['actual_qty', 'projected_qty', 'min_order_qty'];
+
if(!r.exc) {
$.each(r.message, function(k, v) {
- if(!d[k]) d[k] = v;
+ if(!d[k] || in_list(qty_fields, k)) d[k] = v;
});
}
}
@@ -324,6 +348,16 @@
frm.events.get_item_data(frm, item);
},
+ from_warehouse: function(frm, doctype, name) {
+ const item = locals[doctype][name];
+ frm.events.get_item_data(frm, item);
+ },
+
+ warehouse: function(frm, doctype, name) {
+ const item = locals[doctype][name];
+ frm.events.get_item_data(frm, item);
+ },
+
rate: function(frm, doctype, name) {
const item = locals[doctype][name];
frm.events.get_item_data(frm, item);
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index 536f5fa..d1f29e3 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -18,6 +18,8 @@
"amended_from",
"warehouse_section",
"set_warehouse",
+ "column_break5",
+ "set_from_warehouse",
"items_section",
"scan_barcode",
"items",
@@ -287,13 +289,27 @@
"fieldtype": "Link",
"label": "Set Warehouse",
"options": "Warehouse"
+ },
+ {
+ "fieldname": "column_break5",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "print_width": "50%",
+ "width": "50%"
+ },
+ {
+ "depends_on": "eval:doc.material_request_type == 'Material Transfer'",
+ "fieldname": "set_from_warehouse",
+ "fieldtype": "Link",
+ "label": "Set From Warehouse",
+ "options": "Warehouse"
}
],
"icon": "fa fa-ticket",
"idx": 70,
"is_submittable": 1,
"links": [],
- "modified": "2020-03-02 20:21:09.990867",
+ "modified": "2020-05-01 20:21:09.990867",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 739d749..97606f4 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -456,6 +456,9 @@
if source_parent.material_request_type == "Customer Provided":
target.allow_zero_valuation_rate = 1
+ if source_parent.material_request_type == "Material Transfer":
+ target.s_warehouse = obj.from_warehouse
+
def set_missing_values(source, target):
target.purpose = source.material_request_type
if source.job_card:
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json
index 2bdc268..df140ff 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.json
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-02-22 01:28:02",
"doctype": "DocType",
@@ -21,6 +22,7 @@
"quantity_and_warehouse",
"qty",
"stock_uom",
+ "from_warehouse",
"warehouse",
"col_break2",
"uom",
@@ -419,12 +421,19 @@
{
"fieldname": "col_break4",
"fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval:parent.material_request_type == \"Material Transfer\"",
+ "fieldname": "from_warehouse",
+ "fieldtype": "Link",
+ "label": "Source Warehouse (Material Transfer)",
+ "options": "Warehouse"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-16 09:00:00.992835",
+ "modified": "2020-05-01 09:00:00.992835",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request Item",
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 616de5e..231af1a 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -139,7 +139,7 @@
item_location_map[item_doc.item_code] = available_locations
return locations
-def get_available_item_locations(item_code, from_warehouses, required_qty, company):
+def get_available_item_locations(item_code, from_warehouses, required_qty, company, ignore_validation=False):
locations = []
if frappe.get_cached_value('Item', item_code, 'has_serial_no'):
locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty, company)
@@ -152,7 +152,7 @@
remaining_qty = required_qty - total_qty_available
- if remaining_qty > 0:
+ if remaining_qty > 0 and not ignore_validation:
frappe.msgprint(_('{0} units of {1} is not available.')
.format(remaining_qty, frappe.get_desk_link('Item', item_code)))
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index f3020e0..e9568ee 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -92,7 +92,7 @@
refresh: function() {
var me = this;
this._super();
- if(this.frm.doc.docstatus===1) {
+ if(this.frm.doc.docstatus > 0) {
this.show_stock_ledger();
//removed for temporary
this.show_general_ledger();
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index c2b3892..8dfe1d1 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -196,6 +196,7 @@
# because updating ordered qty in bin depends upon updated ordered qty in PO
self.update_stock_ledger()
self.make_gl_entries_on_cancel()
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.delete_auto_created_batches()
def get_current_stock(self):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 40d7cc2..3d42590 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -51,7 +51,7 @@
self.assertEqual(current_bin_stock_value, existing_bin_stock_value + 250)
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
-
+
def test_batched_serial_no_purchase(self):
item = frappe.db.exists("Item", {'item_name': 'Batched Serialized Item'})
if not item:
@@ -68,7 +68,7 @@
pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500)
self.assertTrue(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
-
+
pr.load_from_db()
batch_no = pr.items[0].batch_no
pr.cancel()
@@ -106,7 +106,7 @@
self.assertEqual(expected_values[gle.account][1], gle.credit)
pr.cancel()
- self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
+ self.assertTrue(get_gl_entries("Purchase Receipt", pr.name))
def test_subcontracting(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -375,7 +375,7 @@
location = frappe.db.get_value('Asset', assets[0].name, 'location')
self.assertEquals(location, "Test Location")
-
+
def test_purchase_return_with_submitted_asset(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
@@ -397,10 +397,10 @@
pr_return = make_purchase_return(pr.name)
self.assertRaises(frappe.exceptions.ValidationError, pr_return.submit)
-
+
asset.load_from_db()
asset.cancel()
-
+
pr_return.submit()
def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self):
@@ -505,10 +505,13 @@
self.assertEquals(pi2.items[1].qty, 1)
def test_stock_transfer_from_purchase_receipt(self):
- set_perpetual_inventory(1)
- pr = make_purchase_receipt(do_not_save=1)
+ pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1', company="_Test Company with perpetual inventory")
+
+ pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1", do_not_save=1)
+
pr.supplier_warehouse = ''
- pr.items[0].from_warehouse = '_Test Warehouse 2 - _TC'
+ pr.items[0].from_warehouse = 'Work In Progress - TCP1'
pr.submit()
@@ -518,31 +521,33 @@
self.assertFalse(gl_entries)
expected_sle = {
- '_Test Warehouse 2 - _TC': -5,
- '_Test Warehouse - _TC': 5
+ 'Work In Progress - TCP1': -5,
+ 'Stores - TCP1': 5
}
for sle in sl_entries:
self.assertEqual(expected_sle[sle.warehouse], sle.actual_qty)
- set_perpetual_inventory(0)
-
def test_stock_transfer_from_purchase_receipt_with_valuation(self):
- set_perpetual_inventory(1)
- warehouse = frappe.get_doc('Warehouse', '_Test Warehouse 2 - _TC')
- warehouse.account = '_Test Account Stock In Hand - _TC'
+ warehouse = frappe.get_doc('Warehouse', 'Work In Progress - TCP1')
+ warehouse.account = '_Test Account Stock In Hand - TCP1'
warehouse.save()
- pr = make_purchase_receipt(do_not_save=1)
- pr.items[0].from_warehouse = '_Test Warehouse 2 - _TC'
+ pr1 = make_purchase_receipt(warehouse = 'Work In Progress - TCP1',
+ company="_Test Company with perpetual inventory")
+
+ pr = make_purchase_receipt(company="_Test Company with perpetual inventory",
+ warehouse = "Stores - TCP1", do_not_save=1)
+
+ pr.items[0].from_warehouse = 'Work In Progress - TCP1'
pr.supplier_warehouse = ''
pr.append('taxes', {
'charge_type': 'On Net Total',
- 'account_head': '_Test Account Shipping Charges - _TC',
+ 'account_head': '_Test Account Shipping Charges - TCP1',
'category': 'Valuation and Total',
- 'cost_center': 'Main - _TC',
+ 'cost_center': 'Main - TCP1',
'description': 'Test',
'rate': 9
})
@@ -553,14 +558,14 @@
sl_entries = get_sl_entries('Purchase Receipt', pr.name)
expected_gle = [
- ['Stock In Hand - _TC', 272.5, 0.0],
- ['_Test Account Stock In Hand - _TC', 0.0, 250.0],
- ['_Test Account Shipping Charges - _TC', 0.0, 22.5]
+ ['Stock In Hand - TCP1', 272.5, 0.0],
+ ['_Test Account Stock In Hand - TCP1', 0.0, 250.0],
+ ['_Test Account Shipping Charges - TCP1', 0.0, 22.5]
]
expected_sle = {
- '_Test Warehouse 2 - _TC': -5,
- '_Test Warehouse - _TC': 5
+ 'Work In Progress - TCP1': -5,
+ 'Stores - TCP1': 5
}
for sle in sl_entries:
@@ -573,8 +578,6 @@
warehouse.account = ''
warehouse.save()
- set_perpetual_inventory(0)
-
def get_sl_entries(voucher_type, voucher_no):
return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
@@ -582,7 +585,7 @@
order by posting_time desc""", (voucher_type, voucher_no), as_dict=1)
def get_gl_entries(voucher_type, voucher_no):
- return frappe.db.sql("""select account, debit, credit, cost_center
+ return frappe.db.sql("""select account, debit, credit, cost_center, is_cancelled
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account desc""", (voucher_type, voucher_no), as_dict=1)
diff --git a/erpnext/stock/doctype/purchase_receipt/test_records.json b/erpnext/stock/doctype/purchase_receipt/test_records.json
index e7ea9af..724e3d7 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_records.json
+++ b/erpnext/stock/doctype/purchase_receipt/test_records.json
@@ -83,5 +83,37 @@
}
],
"supplier": "_Test Supplier"
+ },
+
+ {
+ "buying_price_list": "_Test Price List",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "doctype": "Purchase Receipt",
+ "base_grand_total": 5000.0,
+ "is_subcontracted": "Yes",
+ "base_net_total": 5000.0,
+ "items": [
+ {
+ "base_amount": 5000.0,
+ "conversion_factor": 1.0,
+ "description": "_Test FG Item",
+ "doctype": "Purchase Receipt Item",
+ "item_code": "_Test FG Item",
+ "item_name": "_Test FG Item",
+ "parentfield": "items",
+ "qty": 10.0,
+ "rate": 500.0,
+ "received_qty": 10.0,
+ "rejected_qty": 0.0,
+ "stock_uom": "_Test UOM",
+ "uom": "_Test UOM",
+ "warehouse": "_Test Warehouse - _TC",
+ "cost_center": "Main - _TC"
+ }
+ ],
+ "supplier": "_Test Supplier",
+ "supplier_warehouse": "_Test Warehouse - _TC"
}
]
\ No newline at end of file
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index b32c709..914eea3 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -130,13 +130,17 @@
sle_dict = self.get_stock_ledger_entries(serial_no)
if sle_dict:
if sle_dict.get("incoming", []):
- entries["purchase_sle"] = sle_dict["incoming"][0]
+ sle_list = [sle for sle in sle_dict["incoming"] if sle.is_cancelled == 0]
+ if sle_list:
+ entries["purchase_sle"] = sle_list[0]
if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0:
entries["last_sle"] = sle_dict["incoming"][0]
else:
entries["last_sle"] = sle_dict["outgoing"][0]
- entries["delivery_sle"] = sle_dict["outgoing"][0]
+ sle_list = [sle for sle in sle_dict["outgoing"] if sle.is_cancelled == 0]
+ if sle_list:
+ entries["delivery_sle"] = sle_list[0]
return entries
@@ -147,11 +151,11 @@
for sle in frappe.db.sql("""
SELECT voucher_type, voucher_no,
- posting_date, posting_time, incoming_rate, actual_qty, serial_no
+ posting_date, posting_time, incoming_rate, actual_qty, serial_no, is_cancelled
FROM
`tabStock Ledger Entry`
WHERE
- item_code=%s AND company = %s AND ifnull(is_cancelled, 'No')='No'
+ item_code=%s AND company = %s
AND (serial_no = %s
OR serial_no like %s
OR serial_no like %s
@@ -171,7 +175,7 @@
def on_trash(self):
sl_entries = frappe.db.sql("""select serial_no from `tabStock Ledger Entry`
- where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No'""",
+ where serial_no like %s and item_code=%s""",
("%%%s%%" % self.name, self.item_code), as_dict=True)
# Find the exact match
@@ -221,7 +225,7 @@
if serial_nos:
frappe.throw(_("Item {0} is not setup for Serial Nos. Column must be blank").format(sle.item_code),
SerialNoNotRequiredError)
- elif sle.is_cancelled == "No":
+ else:
if serial_nos:
if cint(sle.actual_qty) != flt(sle.actual_qty):
frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
@@ -239,6 +243,10 @@
"delivery_document_no", "delivery_document_type", "warehouse",
"purchase_document_no", "company"], as_dict=1)
+ if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse:
+ frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
+ .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse), SerialNoWarehouseError)
+
if sr.item_code!=sle.item_code:
if not allow_serial_nos_with_different_item(serial_no, sle):
frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
@@ -265,7 +273,7 @@
frappe.throw(_("Serial No {0} does not belong to Batch {1}").format(serial_no,
sle.batch_no), SerialNoBatchError)
- if sle.is_cancelled=="No" and not sr.warehouse:
+ if not sr.warehouse:
frappe.throw(_("Serial No {0} does not belong to any Warehouse")
.format(serial_no), SerialNoWarehouseError)
@@ -311,12 +319,6 @@
elif cint(sle.actual_qty) < 0 or not item_det.serial_no_series:
frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
SerialNoRequiredError)
- elif serial_nos:
- for serial_no in serial_nos:
- sr = frappe.db.get_value("Serial No", serial_no, ["name", "warehouse"], as_dict=1)
- if sr and cint(sle.actual_qty) < 0 and sr.warehouse != sle.warehouse:
- frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
- .format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
def validate_material_transfer_entry(sle_doc):
sle_doc.update({
@@ -324,7 +326,7 @@
"skip_serial_no_validaiton": False
})
- if (sle_doc.voucher_type == "Stock Entry" and sle_doc.is_cancelled == "No" and
+ if (sle_doc.voucher_type == "Stock Entry" and
frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"):
if sle_doc.actual_qty < 0:
sle_doc.skip_update_serial_no = True
@@ -367,7 +369,7 @@
stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no)
if stock_entry.purpose in ("Repack", "Manufacture"):
for d in stock_entry.get("items"):
- if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse):
+ if d.serial_no and (d.s_warehouse or d.t_warehouse):
serial_nos = get_serial_nos(d.serial_no)
if sle_serial_no in serial_nos:
allow_serial_nos = True
@@ -376,7 +378,7 @@
def update_serial_nos(sle, item_det):
if sle.skip_update_serial_no: return
- if sle.is_cancelled == "No" and not sle.serial_no and cint(sle.actual_qty) > 0 \
+ if not sle.serial_no and cint(sle.actual_qty) > 0 \
and item_det.has_serial_no == 1 and item_det.serial_no_series:
serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
frappe.db.set(sle, "serial_no", serial_nos)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 496a865..e4412e0 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -215,9 +215,7 @@
source_doctype: "Material Request",
target: frm,
date_field: "schedule_date",
- setters: {
- company: frm.doc.company,
- },
+ setters: {},
get_query_filters: {
docstatus: 1,
material_request_type: ["in", ["Material Transfer", "Material Issue"]],
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index bdd0bd0..704ae41 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-04-09 11:43:55",
@@ -12,7 +13,6 @@
"stock_entry_type",
"outgoing_stock_entry",
"purpose",
- "company",
"work_order",
"purchase_order",
"delivery_note_no",
@@ -20,6 +20,7 @@
"pick_list",
"purchase_receipt_no",
"col2",
+ "company",
"posting_date",
"posting_time",
"set_posting_time",
@@ -65,6 +66,7 @@
"dimension_col_break",
"printing_settings",
"select_print_heading",
+ "print_settings_col_break",
"letter_head",
"more_info",
"is_opening",
@@ -291,6 +293,7 @@
"fieldtype": "Section Break"
},
{
+ "description": "Sets 'Source Warehouse' in each row of the items table.",
"fieldname": "from_warehouse",
"fieldtype": "Link",
"in_list_view": 1,
@@ -320,6 +323,7 @@
"fieldtype": "Column Break"
},
{
+ "description": "Sets 'Target Warehouse' in each row of the items table.",
"fieldname": "to_warehouse",
"fieldtype": "Link",
"in_list_view": 1,
@@ -622,12 +626,17 @@
"label": "Pick List",
"options": "Pick List",
"read_only": 1
+ },
+ {
+ "fieldname": "print_settings_col_break",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-file-text",
"idx": 1,
"is_submittable": 1,
- "modified": "2019-09-27 14:38:20.801420",
+ "links": [],
+ "modified": "2020-04-23 12:56:52.881752",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 95f9d46..ddf4ec0 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -107,6 +107,9 @@
self.update_work_order()
self.update_stock_ledger()
+
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+
self.make_gl_entries_on_cancel()
self.update_cost_in_project()
self.update_transferred_qty()
@@ -651,7 +654,7 @@
if self.docstatus == 2:
sl_entries.reverse()
- self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
+ self.make_sl_entries(sl_entries)
def get_gl_entries(self, warehouse_account):
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
@@ -674,7 +677,7 @@
multiply_based_on = d.basic_amount if total_basic_amount else d.qty
item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account] += \
- (t.amount * multiply_based_on) / divide_based_on
+ flt(t.amount * multiply_based_on) / divide_based_on
if item_account_wise_additional_cost:
for d in self.get("items"):
@@ -1050,9 +1053,9 @@
fields=["required_qty", "consumed_qty"]
)
- req_qty = flt(req_items[0].required_qty)
+ req_qty = flt(req_items[0].required_qty) if req_items else flt(4)
req_qty_each = flt(req_qty / manufacturing_qty)
- consumed_qty = flt(req_items[0].consumed_qty)
+ consumed_qty = flt(req_items[0].consumed_qty) if req_items else 0
if trans_qty and manufacturing_qty > (produced_qty + flt(self.fg_completed_qty)):
if qty >= req_qty:
diff --git a/erpnext/stock/doctype/stock_entry/test_records.json b/erpnext/stock/doctype/stock_entry/test_records.json
index cfbdce4..dc21287 100644
--- a/erpnext/stock/doctype/stock_entry/test_records.json
+++ b/erpnext/stock/doctype/stock_entry/test_records.json
@@ -24,7 +24,6 @@
{
"company": "_Test Company",
"doctype": "Stock Entry",
- "posting_date": "2013-01-25",
"purpose": "Material Issue",
"stock_entry_type": "Material Issue",
"items": [
@@ -47,7 +46,6 @@
{
"company": "_Test Company",
"doctype": "Stock Entry",
- "posting_date": "2013-01-25",
"purpose": "Material Transfer",
"stock_entry_type": "Material Transfer",
"items": [
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 2afabe1..0fbc631 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -149,10 +149,10 @@
mr.cancel()
- self.assertFalse(frappe.db.sql("""select * from `tabStock Ledger Entry`
+ self.assertTrue(frappe.db.sql("""select * from `tabStock Ledger Entry`
where voucher_type='Stock Entry' and voucher_no=%s""", mr.name))
- self.assertFalse(frappe.db.sql("""select * from `tabGL Entry`
+ self.assertTrue(frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Stock Entry' and voucher_no=%s""", mr.name))
def test_material_issue_gl_entry(self):
@@ -178,12 +178,6 @@
)
mi.cancel()
- self.assertFalse(frappe.db.sql("""select name from `tabStock Ledger Entry`
- where voucher_type='Stock Entry' and voucher_no=%s""", mi.name))
-
- self.assertFalse(frappe.db.sql("""select name from `tabGL Entry`
- where voucher_type='Stock Entry' and voucher_no=%s""", mi.name))
-
def test_material_transfer_gl_entry(self):
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
@@ -216,11 +210,6 @@
)
mtn.cancel()
- self.assertFalse(frappe.db.sql("""select * from `tabStock Ledger Entry`
- where voucher_type='Stock Entry' and voucher_no=%s""", mtn.name))
-
- self.assertFalse(frappe.db.sql("""select * from `tabGL Entry`
- where voucher_type='Stock Entry' and voucher_no=%s""", mtn.name))
def test_repack_no_change_in_valuation(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
@@ -544,10 +533,10 @@
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto", '')
# test freeze_stocks_upto_days
- frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 7)
+ frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", -1)
se = frappe.copy_doc(test_records[0])
se.set_posting_time = 1
- se.posting_date = add_days(nowdate(), -15)
+ se.posting_date = nowdate()
se.set_stock_entry_type()
se.insert()
self.assertRaises(StockFreezeError, se.submit)
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index a848c80..c16a41c 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -14,12 +14,12 @@
"t_warehouse",
"sec_break1",
"item_code",
- "item_group",
"col_break2",
"item_name",
"section_break_8",
"description",
"column_break_10",
+ "item_group",
"image",
"image_view",
"quantity_and_rate",
@@ -178,6 +178,7 @@
"bold": 1,
"fieldname": "basic_rate",
"fieldtype": "Currency",
+ "in_list_view": 1,
"label": "Basic Rate (as per Stock UOM)",
"oldfieldname": "incoming_rate",
"oldfieldtype": "Currency",
@@ -420,6 +421,7 @@
"options": "Item"
},
{
+ "collapsible": 1,
"fieldname": "reference_section",
"fieldtype": "Section Break",
"label": "Reference"
@@ -466,7 +468,6 @@
"fetch_from": "item_code.item_group",
"fieldname": "item_group",
"fieldtype": "Data",
- "in_list_view": 1,
"label": "Item Group"
},
{
@@ -495,7 +496,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-03-19 12:34:09.836295",
+ "modified": "2020-04-23 19:19:28.539769",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index c03eb79..fda17e0 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_copy": 1,
"autoname": "MAT-SLE-.YYYY.-.#####",
"creation": "2013-01-29 19:25:42",
@@ -255,11 +256,10 @@
"width": "150px"
},
{
+ "default": "0",
"fieldname": "is_cancelled",
- "fieldtype": "Select",
- "hidden": 1,
+ "fieldtype": "Check",
"label": "Is Cancelled",
- "options": "\nNo\nYes",
"report_hide": 1
},
{
@@ -275,7 +275,8 @@
"icon": "fa fa-list",
"idx": 1,
"in_create": 1,
- "modified": "2020-02-25 22:53:33.504681",
+ "links": [],
+ "modified": "2020-04-23 05:57:03.985520",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index dab5a7b..101c6e0 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -46,7 +46,7 @@
def calculate_batch_qty(self):
if self.batch_no:
batch_qty = frappe.db.get_value("Stock Ledger Entry",
- {"docstatus": 1, "batch_no": self.batch_no, "is_cancelled": "No"},
+ {"docstatus": 1, "batch_no": self.batch_no},
"sum(actual_qty)") or 0
frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty)
@@ -93,7 +93,7 @@
elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item))
- elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No":
+ elif item_det.has_batch_no ==0 and self.batch_no:
frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
if item_det.has_variants:
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 1791978..dd284e4 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -227,7 +227,7 @@
},
refresh: function() {
- if(this.frm.doc.docstatus==1) {
+ if(this.frm.doc.docstatus > 0) {
this.show_stock_ledger();
if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
this.show_general_ledger();
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 0a49c26..5e469c2 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -6,7 +6,6 @@
import frappe.defaults
from frappe import msgprint, _
from frappe.utils import cstr, flt, cint
-from erpnext.stock.stock_ledger import update_entries_after
from erpnext.controllers.stock_controller import StockController
from erpnext.accounts.utils import get_company_default
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -43,7 +42,8 @@
update_serial_nos_after_submit(self, "items")
def on_cancel(self):
- self.delete_and_repost_sle()
+ self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+ self.make_sle_on_cancel()
self.make_gl_entries_on_cancel()
def remove_items_with_no_change(self):
@@ -193,6 +193,7 @@
if row.serial_no or row.batch_no:
frappe.throw(_("Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.") \
.format(row.idx, frappe.bold(row.item_code)))
+
previous_sle = get_previous_sle({
"item_code": row.item_code,
"warehouse": row.warehouse,
@@ -319,7 +320,7 @@
"voucher_detail_no": row.name,
"company": self.company,
"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
- "is_cancelled": "No" if self.docstatus != 2 else "Yes",
+ "is_cancelled": 1 if self.docstatus == 2 else 0,
"serial_no": '\n'.join(serial_nos) if serial_nos else '',
"batch_no": row.batch_no,
"valuation_rate": flt(row.valuation_rate, row.precision("valuation_rate"))
@@ -328,27 +329,35 @@
if not row.batch_no:
data.qty_after_transaction = flt(row.qty, row.precision("qty"))
+ if self.docstatus == 2 and not row.batch_no:
+ if row.current_qty:
+ data.actual_qty = -1 * row.current_qty
+ data.qty_after_transaction = flt(row.current_qty)
+ data.valuation_rate = flt(row.current_valuation_rate)
+ data.stock_value = data.qty_after_transaction * data.valuation_rate
+ data.stock_value_difference = -1 * flt(row.amount_difference)
+ else:
+ data.actual_qty = row.qty
+ data.qty_after_transaction = 0.0
+ data.valuation_rate = flt(row.valuation_rate)
+ data.stock_value_difference = -1 * flt(row.amount_difference)
+
return data
- def delete_and_repost_sle(self):
- """ Delete Stock Ledger Entries related to this voucher
- and repost future Stock Ledger Entries"""
-
- existing_entries = frappe.db.sql("""select distinct item_code, warehouse
- from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
- (self.doctype, self.name), as_dict=1)
-
- # delete entries
- frappe.db.sql("""delete from `tabStock Ledger Entry`
- where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name))
-
+ def make_sle_on_cancel(self):
sl_entries = []
has_serial_no = False
for row in self.items:
if row.serial_no or row.batch_no or row.current_serial_no:
has_serial_no = True
- self.get_sle_for_serialized_items(row, sl_entries)
+ serial_nos = ''
+ if row.current_serial_no:
+ serial_nos = get_serial_nos(row.current_serial_no)
+
+ sl_entries.append(self.get_sle_for_items(row, serial_nos))
+ else:
+ sl_entries.append(self.get_sle_for_items(row))
if sl_entries:
if has_serial_no:
@@ -358,14 +367,6 @@
allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
- # repost future entries for selected item_code, warehouse
- for entries in existing_entries:
- update_entries_after({
- "item_code": entries.item_code,
- "warehouse": entries.warehouse,
- "posting_date": self.posting_date,
- "posting_time": self.posting_time
- })
def merge_similar_item_serial_nos(self, sl_entries):
# If user has put the same item in multiple row with different serial no
@@ -434,12 +435,6 @@
else:
self._submit()
- def cancel(self):
- if len(self.items) > 100:
- self.queue_action('cancel')
- else:
- self._cancel()
-
@frappe.whitelist()
def get_items(warehouse, posting_date, posting_time, company):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 51d027f..1571416 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -34,11 +34,11 @@
# [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [
- [50, 1000, "2012-12-26", "12:00"],
- [25, 900, "2012-12-26", "12:00"],
- ["", 1000, "2012-12-20", "12:05"],
- [20, "", "2012-12-26", "12:05"],
- [0, "", "2012-12-31", "12:10"]
+ [50, 1000],
+ [25, 900],
+ ["", 1000],
+ [20, ""],
+ [0, ""]
]
for d in input_data:
@@ -47,13 +47,13 @@
last_sle = get_previous_sle({
"item_code": "_Test Item",
"warehouse": "Stores - TCP1",
- "posting_date": d[2],
- "posting_time": d[3]
+ "posting_date": nowdate(),
+ "posting_time": nowtime()
})
# submit stock reconciliation
stock_reco = create_stock_reconciliation(qty=d[0], rate=d[1],
- posting_date=d[2], posting_time=d[3], warehouse="Stores - TCP1",
+ posting_date=nowdate(), posting_time=nowtime(), warehouse="Stores - TCP1",
company=company, expense_account = "Stock Adjustment - TCP1")
# check stock value
@@ -68,8 +68,8 @@
and valuation_rate == last_sle.get("valuation_rate"):
self.assertFalse(sle)
else:
- self.assertEqual(sle[0].qty_after_transaction, qty_after_transaction)
- self.assertEqual(sle[0].stock_value, qty_after_transaction * valuation_rate)
+ self.assertEqual(flt(sle[0].qty_after_transaction, 1), flt(qty_after_transaction, 1))
+ self.assertEqual(flt(sle[0].stock_value, 1), flt(qty_after_transaction * valuation_rate, 1))
# no gl entries
self.assertTrue(frappe.db.get_value("Stock Ledger Entry",
@@ -77,16 +77,10 @@
acc_bal, stock_bal, wh_list = get_stock_and_account_balance("Stock In Hand - TCP1",
stock_reco.posting_date, stock_reco.company)
- self.assertEqual(acc_bal, stock_bal)
+ self.assertEqual(flt(acc_bal, 1), flt(stock_bal, 1))
stock_reco.cancel()
- self.assertFalse(frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
-
- self.assertFalse(frappe.db.get_value("GL Entry",
- {"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
-
def test_get_items(self):
create_warehouse("_Test Warehouse Group 1", {"is_group": 1})
create_warehouse("_Test Warehouse Ledger 1",
@@ -113,7 +107,6 @@
sr = create_stock_reconciliation(item_code=serial_item_code,
warehouse = serial_warehouse, qty=5, rate=200)
- # print(sr.name)
serial_nos = get_serial_nos(sr.items[0].serial_no)
self.assertEqual(len(serial_nos), 5)
@@ -133,7 +126,6 @@
sr = create_stock_reconciliation(item_code=serial_item_code,
warehouse = serial_warehouse, qty=5, rate=300, serial_no = '\n'.join(serial_nos))
- # print(sr.name)
serial_nos1 = get_serial_nos(sr.items[0].serial_no)
self.assertEqual(len(serial_nos1), 5)
@@ -155,10 +147,6 @@
stock_doc = frappe.get_doc("Stock Reconciliation", d)
stock_doc.cancel()
- for d in serial_nos + serial_nos1:
- if frappe.db.exists("Serial No", d):
- frappe.delete_doc("Serial No", d)
-
def test_stock_reco_for_batch_item(self):
set_perpetual_inventory()
@@ -208,13 +196,13 @@
def insert_existing_sle(warehouse):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
- make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item",
+ make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item",
target=warehouse, qty=10, basic_rate=700)
- make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
+ make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item",
source=warehouse, qty=15)
- make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
+ make_stock_entry(posting_date=nowdate(), posting_time=nowtime(), item_code="_Test Item",
target=warehouse, qty=15, basic_rate=1200)
def create_batch_or_serial_no_items():
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 61429cc..d50712a 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -77,7 +77,11 @@
if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args))
- if out.get("warehouse"):
+ if (args.get("doctype") == "Material Request" and
+ args.get("material_request_type") == "Material Transfer"):
+ out.update(get_bin_details(args.item_code, args.get("from_warehouse")))
+
+ elif out.get("warehouse"):
out.update(get_bin_details(args.item_code, out.warehouse))
# update args with out, if key or value not exists
@@ -342,8 +346,14 @@
out["manufacturer_part_no"] = None
out["manufacturer"] = None
else:
- out["manufacturer"], out["manufacturer_part_no"] = frappe.get_value("Item", item.name,
- ["default_item_manufacturer", "default_manufacturer_part_no"] )
+ data = frappe.get_value("Item", item.name,
+ ["default_item_manufacturer", "default_manufacturer_part_no"] , as_dict=1)
+
+ if data:
+ out.update({
+ "manufacturer": data.default_item_manufacturer,
+ "manufacturer_part_no": data.default_manufacturer_part_no
+ })
child_doctype = args.doctype + ' Item'
meta = frappe.get_meta(child_doctype)
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.js b/erpnext/stock/report/stock_ledger/stock_ledger.js
index 9adfbf7..6f12c27 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.js
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.js
@@ -32,7 +32,7 @@
"options": "Warehouse",
"get_query": function() {
const company = frappe.query_report.get_filter_value('company');
- return {
+ return {
filters: { 'company': company }
}
}
@@ -82,6 +82,11 @@
"label": __("Include UOM"),
"fieldtype": "Link",
"options": "UOM"
+ },
+ {
+ "fieldname": "show_cancelled_entries",
+ "label": __("Show Cancelled Entries"),
+ "fieldtype": "Check"
}
],
"formatter": function (value, row, column, data, default_formatter) {
@@ -96,9 +101,3 @@
return value;
},
}
-
-// $(function() {
-// $(wrapper).bind("show", function() {
-// frappe.query_report.load();
-// });
-// });
\ No newline at end of file
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 28d7208..abf959e 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -46,19 +46,6 @@
"out_qty": min(sle.actual_qty, 0)
})
- # get the name of the item that was produced using this item
- if sle.voucher_type == "Stock Entry":
- purpose, work_order, fg_completed_qty = frappe.db.get_value(sle.voucher_type, sle.voucher_no, ["purpose", "work_order", "fg_completed_qty"])
-
- if purpose == "Manufacture" and work_order:
- finished_product = frappe.db.get_value("Work Order", work_order, "item_name")
- finished_qty = fg_completed_qty
-
- sle.update({
- "finished_product": finished_product,
- "finished_qty": finished_qty,
- })
-
data.append(sle)
if include_uom:
@@ -77,8 +64,6 @@
{"label": _("In Qty"), "fieldname": "in_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
{"label": _("Out Qty"), "fieldname": "out_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"},
{"label": _("Balance Qty"), "fieldname": "qty_after_transaction", "fieldtype": "Float", "width": 100, "convertible": "qty"},
- {"label": _("Finished Product"), "fieldname": "finished_product", "width": 100},
- {"label": _("Finished Qty"), "fieldname": "finished_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"},
{"label": _("Voucher #"), "fieldname": "voucher_no", "fieldtype": "Dynamic Link", "options": "voucher_type", "width": 150},
{"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 150},
{"label": _("Item Group"), "fieldname": "item_group", "fieldtype": "Link", "options": "Item Group", "width": 100},
@@ -196,6 +181,9 @@
if filters.get("project"):
conditions.append("project=%(project)s")
+ if not filters.get("show_cancelled_entries"):
+ conditions.append("is_cancelled = 0")
+
return "and {}".format(" and ".join(conditions)) if conditions else ""
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index 5697315..b5ae1b7 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -6,7 +6,6 @@
from frappe.utils import flt, cstr, nowdate, nowtime
from erpnext.stock.utils import update_bin
from erpnext.stock.stock_ledger import update_entries_after
-from erpnext.controllers.stock_controller import update_gl_entries_after
def repost(only_actual=False, allow_negative_stock=False, allow_zero_rate=False, only_bin=False):
"""
@@ -56,12 +55,13 @@
update_bin_qty(item_code, warehouse, qty_dict)
-def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False): update_entries_after({ "item_code": item_code, "warehouse": warehouse },
+def repost_actual_qty(item_code, warehouse, allow_zero_rate=False, allow_negative_stock=False):
+ update_entries_after({ "item_code": item_code, "warehouse": warehouse },
allow_zero_rate=allow_zero_rate, allow_negative_stock=allow_negative_stock)
def get_balance_qty_from_sle(item_code, warehouse):
balance_qty = frappe.db.sql("""select qty_after_transaction from `tabStock Ledger Entry`
- where item_code=%s and warehouse=%s and is_cancelled='No'
+ where item_code=%s and warehouse=%s
order by posting_date desc, posting_time desc, creation desc
limit 1""", (item_code, warehouse))
@@ -191,7 +191,7 @@
print(d[0], d[1], d[2], serial_nos[0][0])
sle = frappe.db.sql("""select valuation_rate, company from `tabStock Ledger Entry`
- where item_code = %s and warehouse = %s and ifnull(is_cancelled, 'No') = 'No'
+ where item_code = %s and warehouse = %s
order by posting_date desc limit 1""", (d[0], d[1]))
sle_dict = {
@@ -208,7 +208,6 @@
'stock_uom' : d[3],
'incoming_rate' : sle and flt(serial_nos[0][0]) > flt(d[2]) and flt(sle[0][0]) or 0,
'company' : sle and cstr(sle[0][1]) or 0,
- 'is_cancelled' : 'No',
'batch_no' : '',
'serial_no' : ''
}
@@ -220,8 +219,7 @@
args = sle_dict.copy()
args.update({
- "sle_id": sle_doc.name,
- "is_amended": 'No'
+ "sle_id": sle_doc.name
})
update_bin(args)
@@ -246,15 +244,3 @@
sr.save()
except:
pass
-
-def repost_gle_for_stock_transactions(posting_date=None, posting_time=None, for_warehouses=None):
- frappe.db.auto_commit_on_many_writes = 1
-
- if not posting_date:
- posting_date = "1900-01-01"
- if not posting_time:
- posting_time = "00:00"
-
- update_gl_entries_after(posting_date, posting_time, for_warehouses=for_warehouses)
-
- frappe.db.auto_commit_on_many_writes = 0
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 7567a1a..b4cb8ca 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -4,8 +4,8 @@
import frappe, erpnext
from frappe import _
-from frappe.utils import cint, flt, cstr, now
-from erpnext.stock.utils import get_valuation_method
+from frappe.utils import cint, flt, cstr, now, now_datetime
+from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel
import json
from six import iteritems
@@ -16,36 +16,48 @@
_exceptions = frappe.local('stockledger_exceptions')
# _exceptions = []
-def make_sl_entries(sl_entries, is_amended=None, allow_negative_stock=False, via_landed_cost_voucher=False):
+def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
if sl_entries:
from erpnext.stock.utils import update_bin
- cancel = True if sl_entries[0].get("is_cancelled") == "Yes" else False
+ cancel = sl_entries[0].get("is_cancelled")
if cancel:
- set_as_cancel(sl_entries[0].get('voucher_no'), sl_entries[0].get('voucher_type'))
+ set_as_cancel(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
for sle in sl_entries:
sle_id = None
- if sle.get('is_cancelled') == 'Yes':
- sle['actual_qty'] = -flt(sle['actual_qty'])
+ if via_landed_cost_voucher or cancel:
+ sle['posting_date'] = now_datetime().strftime('%Y-%m-%d')
+ sle['posting_time'] = now_datetime().strftime('%H:%M:%S.%f')
+
+ if cancel:
+ sle['actual_qty'] = -flt(sle.get('actual_qty'), 0)
+
+ if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
+ sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
+ sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
+ sle['incoming_rate'] = 0.0
+
+ if sle['actual_qty'] > 0 and not sle.get('incoming_rate'):
+ sle['incoming_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,
+ sle.voucher_type, sle.voucher_no, sle.voucher_detail_no)
+ sle['outgoing_rate'] = 0.0
+
if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
sle_id = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
args = sle.copy()
args.update({
- "sle_id": sle_id,
- "is_amended": is_amended
+ "sle_id": sle_id
})
update_bin(args, allow_negative_stock, via_landed_cost_voucher)
- if cancel:
- delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
def set_as_cancel(voucher_type, voucher_no):
- frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
+ frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled=1,
modified=%s, modified_by=%s
- where voucher_no=%s and voucher_type=%s""",
+ where voucher_type=%s and voucher_no=%s and is_cancelled = 0""",
(now(), frappe.session.user, voucher_type, voucher_no))
def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
@@ -58,9 +70,6 @@
sle.submit()
return sle.name
-def delete_cancelled_entry(voucher_type, voucher_no):
- frappe.db.sql("""delete from `tabStock Ledger Entry`
- where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
class update_entries_after(object):
"""
@@ -106,14 +115,17 @@
self.stock_queue = json.loads(self.previous_sle.stock_queue or "[]")
self.valuation_method = get_valuation_method(self.item_code)
self.stock_value_difference = 0.0
- self.build()
+ self.build(args.get('sle_id'))
- def build(self):
- # includes current entry!
- entries_to_fix = self.get_sle_after_datetime()
-
- for sle in entries_to_fix:
+ def build(self, sle_id):
+ if sle_id:
+ sle = get_sle_by_id(sle_id)
self.process_sle(sle)
+ else:
+ # includes current entry!
+ entries_to_fix = self.get_sle_after_datetime()
+ for sle in entries_to_fix:
+ self.process_sle(sle)
if self.exceptions:
self.raise_exceptions()
@@ -403,7 +415,10 @@
def get_sle_before_datetime(self):
"""get previous stock ledger entry before current time-bucket"""
- return get_stock_ledger_entries(self.args, "<", "desc", "limit 1", for_update=False)
+ if self.args.get('sle_id'):
+ self.args['name'] = self.args.get('sle_id')
+
+ return get_stock_ledger_entries(self.args, "<=", "desc", "limit 1", for_update=False)
def get_sle_after_datetime(self):
"""get Stock Ledger Entries after a particular datetime, for reposting"""
@@ -470,9 +485,10 @@
if operator in (">", "<=") and previous_sle.get("name"):
conditions += " and name!=%(name)s"
- return frappe.db.sql("""select *, timestamp(posting_date, posting_time) as "timestamp" from `tabStock Ledger Entry`
+ return frappe.db.sql("""
+ select *, timestamp(posting_date, posting_time) as "timestamp"
+ from `tabStock Ledger Entry`
where item_code = %%(item_code)s
- and ifnull(is_cancelled, 'No')='No'
%(conditions)s
order by timestamp(posting_date, posting_time) %(order)s, creation %(order)s
%(limit)s %(for_update)s""" % {
@@ -482,6 +498,11 @@
"order": order
}, previous_sle, as_dict=1, debug=debug)
+def get_sle_by_id(sle_id):
+ return frappe.db.get_all('Stock Ledger Entry',
+ fields=['*', 'timestamp(posting_date, posting_time) as timestamp'],
+ filters={'name': sle_id})[0]
+
def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
allow_zero_rate=False, currency=None, company=None, raise_error_if_no_rate=True):
# Get valuation rate from last sle for the same item and warehouse
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 7f32b8d..f21dc3f 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -364,4 +364,16 @@
else:
row[data.converted_col] = flt(value_before_conversion) / conversion_factor
- result[row_idx] = row
\ No newline at end of file
+ result[row_idx] = row
+
+def get_incoming_outgoing_rate_for_cancel(item_code, voucher_type, voucher_no, voucher_detail_no):
+ outgoing_rate = frappe.db.sql("""SELECT abs(stock_value_difference / actual_qty)
+ FROM `tabStock Ledger Entry`
+ WHERE voucher_type = %s and voucher_no = %s
+ and item_code = %s and voucher_detail_no = %s
+ ORDER BY CREATION DESC limit 1""",
+ (voucher_type, voucher_no, item_code, voucher_detail_no))
+
+ outgoing_rate = outgoing_rate[0][0] if outgoing_rate else 0.0
+
+ return outgoing_rate
\ No newline at end of file
diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py
index 14674c0..ea96503 100644
--- a/erpnext/utilities/transaction_base.py
+++ b/erpnext/utilities/transaction_base.py
@@ -5,8 +5,9 @@
import frappe
import frappe.share
from frappe import _
-from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_link_to_form
+from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form
from erpnext.controllers.status_updater import StatusUpdater
+from erpnext.accounts.utils import get_fiscal_year
from six import string_types
@@ -28,6 +29,8 @@
except ValueError:
frappe.throw(_('Invalid Posting Time'))
+ self.validate_with_last_transaction_posting_time()
+
def add_calendar_event(self, opts, force=False):
if cstr(self.contact_by) != cstr(self._prev.contact_by) or \
cstr(self.contact_date) != cstr(self._prev.contact_date) or force or \
@@ -148,6 +151,30 @@
return ret
+ def validate_with_last_transaction_posting_time(self):
+
+ if self.doctype not in ["Sales Invoice", "Purchase Invoice", "Stock Entry", "Stock Reconciliation",
+ "Delivery Note", "Purchase Receipt", "Fees"]:
+ return
+
+ if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
+ if not (self.get("update_stock") or self.get("is_pos")):
+ return
+
+ fiscal_year = get_fiscal_year(self.get('posting_date'), as_dict=True).name
+
+ last_transaction_time = frappe.db.sql("""
+ select MAX(timestamp(posting_date, posting_time)) as posting_time
+ from `tabStock Ledger Entry`
+ where docstatus = 1 and fiscal_year = %s""", (fiscal_year))[0][0]
+
+ cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
+
+ if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
+ frappe.throw(_("""Posting timestamp of current transaction
+ must be after last Stock transaction's timestamp which is {0}""").format(frappe.bold(last_transaction_time)),
+ title=_("Backdated Stock Entry"))
+
def delete_events(ref_type, ref_name):
events = frappe.db.sql_list(""" SELECT
distinct `tabEvent`.name