Merge branch 'develop' into fix-so-cancellation-message
diff --git a/.github/helper/.flake8_strict b/.github/helper/.flake8_strict
index 4c7f5f8..a79137d 100644
--- a/.github/helper/.flake8_strict
+++ b/.github/helper/.flake8_strict
@@ -1,6 +1,8 @@
 [flake8]
 ignore =
     B007,
+    B009,
+    B010,
     B950,
     E101,
     E111,
diff --git a/.github/helper/install.sh b/.github/helper/install.sh
index ac623e9..85f146d 100644
--- a/.github/helper/install.sh
+++ b/.github/helper/install.sh
@@ -37,6 +37,9 @@
 sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
 
 bench get-app erpnext "${GITHUB_WORKSPACE}"
+
+if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi
+
 bench start &> bench_run_logs.txt &
 bench --site test_site reinstall --yes
 bench build --app frappe
diff --git a/.github/helper/semgrep_rules/README.md b/.github/helper/semgrep_rules/README.md
deleted file mode 100644
index 670d8d2..0000000
--- a/.github/helper/semgrep_rules/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Semgrep linting
-
-## What is semgrep?
-Semgrep or "semantic grep" is language agnostic static analysis tool. In simple terms semgrep is syntax-aware `grep`, so unlike regex it doesn't get confused by different ways of writing same thing or whitespaces or code split in multiple lines etc.
-
-Example:
-
-To check if a translate function is using f-string or not the regex would be `r"_\(\s*f[\"']"` while equivalent rule in semgrep would be `_(f"...")`. As semgrep knows grammer of language it takes care of unnecessary whitespace, type of quotation marks etc.
-
-You can read more such examples in `.github/helper/semgrep_rules` directory.
-
-# Why/when to use this?
-We want to maintain quality of contributions, at the same time remembering all the good practices can be pain to deal with while evaluating contributions. Using semgrep if you can translate "best practice" into a rule then it can automate the task for us.
-
-## Running locally
-
-Install semgrep using homebrew `brew install semgrep` or pip `pip install semgrep`.
-
-To run locally use following command:
-
-`semgrep --config=.github/helper/semgrep_rules [file/folder names]`
-
-## Testing
-semgrep allows testing the tests. Refer to this page: https://semgrep.dev/docs/writing-rules/testing-rules/
-
-When writing new rules you should write few positive and few negative cases as shown in the guide and current tests.
-
-To run current tests: `semgrep --test --test-ignore-todo .github/helper/semgrep_rules`
-
-
-## Reference
-
-If you are new to Semgrep read following pages to get started on writing/modifying rules:
-
-- https://semgrep.dev/docs/getting-started/
-- https://semgrep.dev/docs/writing-rules/rule-syntax
-- https://semgrep.dev/docs/writing-rules/pattern-examples/
-- https://semgrep.dev/docs/writing-rules/rule-ideas/#common-use-cases
diff --git a/.github/helper/semgrep_rules/frappe_correctness.py b/.github/helper/semgrep_rules/frappe_correctness.py
deleted file mode 100644
index 83d4acf..0000000
--- a/.github/helper/semgrep_rules/frappe_correctness.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import frappe
-from frappe import _
-
-from frappe.model.document import Document
-
-
-# ruleid: frappe-modifying-but-not-comitting
-def on_submit(self):
-	if self.value_of_goods == 0:
-		frappe.throw(_('Value of goods cannot be 0'))
-	self.status = 'Submitted'
-
-
-# ok: frappe-modifying-but-not-comitting
-def on_submit(self):
-	if self.value_of_goods == 0:
-		frappe.throw(_('Value of goods cannot be 0'))
-	self.status = 'Submitted'
-	self.db_set('status', 'Submitted')
-
-# ok: frappe-modifying-but-not-comitting
-def on_submit(self):
-	if self.value_of_goods == 0:
-		frappe.throw(_('Value of goods cannot be 0'))
-	x = "y"
-	self.status = x
-	self.db_set('status', x)
-
-
-# ok: frappe-modifying-but-not-comitting
-def on_submit(self):
-	x = "y"
-	self.status = x
-	self.save()
-
-# ruleid: frappe-modifying-but-not-comitting-other-method
-class DoctypeClass(Document):
-	def on_submit(self):
-		self.good_method()
-		self.tainted_method()
-
-	def tainted_method(self):
-		self.status = "uptate"
-
-
-# ok: frappe-modifying-but-not-comitting-other-method
-class DoctypeClass(Document):
-	def on_submit(self):
-		self.good_method()
-		self.tainted_method()
-
-	def tainted_method(self):
-		self.status = "update"
-		self.db_set("status", "update")
-
-# ok: frappe-modifying-but-not-comitting-other-method
-class DoctypeClass(Document):
-	def on_submit(self):
-		self.good_method()
-		self.tainted_method()
-		self.save()
-
-	def tainted_method(self):
-		self.status = "uptate"
diff --git a/.github/helper/semgrep_rules/frappe_correctness.yml b/.github/helper/semgrep_rules/frappe_correctness.yml
deleted file mode 100644
index d9603e8..0000000
--- a/.github/helper/semgrep_rules/frappe_correctness.yml
+++ /dev/null
@@ -1,133 +0,0 @@
-# This file specifies rules for correctness according to how frappe doctype data model works.
-
-rules:
-- id: frappe-modifying-but-not-comitting
-  patterns:
-    - pattern: |
-        def $METHOD(self, ...):
-          ...
-          self.$ATTR = ...
-    - pattern-not: |
-        def $METHOD(self, ...):
-          ...
-          self.$ATTR = ...
-          ...
-          self.db_set(..., self.$ATTR, ...)
-    - pattern-not: |
-        def $METHOD(self, ...):
-          ...
-          self.$ATTR = $SOME_VAR
-          ...
-          self.db_set(..., $SOME_VAR, ...)
-    - pattern-not: |
-        def $METHOD(self, ...):
-          ...
-          self.$ATTR = $SOME_VAR
-          ...
-          self.save()
-    - metavariable-regex:
-        metavariable: '$ATTR'
-        # this is negative look-ahead, add more attrs to ignore like (ignore|ignore_this_too|ignore_me)
-        regex: '^(?!ignore_linked_doctypes|status_updater)(.*)$'
-    - metavariable-regex:
-        metavariable: "$METHOD"
-        regex: "(on_submit|on_cancel)"
-  message: |
-    DocType modified in self.$METHOD. Please check if modification of self.$ATTR is commited to database.
-  languages: [python]
-  severity: ERROR
-
-- id: frappe-modifying-but-not-comitting-other-method
-  patterns:
-  - pattern: |
-      class $DOCTYPE(...):
-        def $METHOD(self, ...):
-          ...
-          self.$ANOTHER_METHOD()
-          ...
-
-        def $ANOTHER_METHOD(self, ...):
-          ...
-          self.$ATTR = ...
-  - pattern-not: |
-      class $DOCTYPE(...):
-        def $METHOD(self, ...):
-          ...
-          self.$ANOTHER_METHOD()
-          ...
-
-        def $ANOTHER_METHOD(self, ...):
-          ...
-          self.$ATTR = ...
-          ...
-          self.db_set(..., self.$ATTR, ...)
-  - pattern-not: |
-      class $DOCTYPE(...):
-        def $METHOD(self, ...):
-          ...
-          self.$ANOTHER_METHOD()
-          ...
-
-        def $ANOTHER_METHOD(self, ...):
-          ...
-          self.$ATTR = $SOME_VAR
-          ...
-          self.db_set(..., $SOME_VAR, ...)
-  - pattern-not: |
-      class $DOCTYPE(...):
-        def $METHOD(self, ...):
-          ...
-          self.$ANOTHER_METHOD()
-          ...
-          self.save()
-        def $ANOTHER_METHOD(self, ...):
-          ...
-          self.$ATTR = ...
-  - metavariable-regex:
-      metavariable: "$METHOD"
-      regex: "(on_submit|on_cancel)"
-  message: |
-    self.$ANOTHER_METHOD is called from self.$METHOD, check if changes to self.$ATTR are commited to database.
-  languages: [python]
-  severity: ERROR
-
-- id: frappe-print-function-in-doctypes
-  pattern: print(...)
-  message: |
-      Did you mean to leave this print statement in? Consider using msgprint or logger instead of print statement.
-  languages: [python]
-  severity: WARNING
-  paths:
-      include:
-        - "*/**/doctype/*"
-
-- id: frappe-modifying-child-tables-while-iterating
-  pattern-either:
-    - pattern: |
-        for $ROW in self.$TABLE:
-            ...
-            self.remove(...)
-    - pattern: |
-        for $ROW in self.$TABLE:
-            ...
-            self.append(...)
-  message: |
-      Child table being modified while iterating on it.
-  languages: [python]
-  severity: ERROR
-  paths:
-      include:
-        - "*/**/doctype/*"
-
-- id: frappe-same-key-assigned-twice
-  pattern-either:
-    - pattern: |
-        {..., $X: $A, ..., $X: $B, ...}
-    - pattern: |
-        dict(..., ($X, $A), ..., ($X, $B), ...)
-    - pattern: |
-        _dict(..., ($X, $A), ..., ($X, $B), ...)
-  message: |
-      key `$X` is uselessly assigned twice. This could be a potential bug.
-  languages: [python]
-  severity: ERROR
diff --git a/.github/helper/semgrep_rules/report.py b/.github/helper/semgrep_rules/report.py
deleted file mode 100644
index ff27840..0000000
--- a/.github/helper/semgrep_rules/report.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from frappe import _
-
-
-# ruleid: frappe-missing-translate-function-in-report-python
-{"label": "Field Label"}
-
-# ruleid: frappe-missing-translate-function-in-report-python
-dict(label="Field Label")
-
-
-# ok: frappe-missing-translate-function-in-report-python
-{"label": _("Field Label")}
-
-# ok: frappe-missing-translate-function-in-report-python
-dict(label=_("Field Label"))
diff --git a/.github/helper/semgrep_rules/report.yml b/.github/helper/semgrep_rules/report.yml
deleted file mode 100644
index f2a9b16..0000000
--- a/.github/helper/semgrep_rules/report.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-rules:
-- id: frappe-missing-translate-function-in-report-python
-  paths:
-    include:
-    - "**/report"
-    exclude:
-    - "**/regional"
-  pattern-either:
-  - patterns:
-      - pattern: |
-          {..., "label": "...", ...}
-      - pattern-not: |
-          {..., "label": _("..."), ...}
-  - patterns:
-      - pattern: dict(..., label="...", ...)
-      - pattern-not: dict(..., label=_("..."), ...)
-  message: |
-      All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
-  languages: [python]
-  severity: ERROR
-
-- id: frappe-translated-values-in-business-logic
-  paths:
-    include:
-    - "**/report"
-  patterns:
-    - pattern-inside: |
-        {..., filters: [...], ...}
-    - pattern: |
-        {..., options: [..., __("..."), ...], ...}
-  message: |
-      Using translated values in options field will require you to translate the values while comparing in business logic. Instead of passing translated labels provide objects that contain both label and value. e.g. { label: __("Option value"), value: "Option value"}
-  languages: [javascript]
-  severity: ERROR
diff --git a/.github/helper/semgrep_rules/security.py b/.github/helper/semgrep_rules/security.py
deleted file mode 100644
index f477d7c..0000000
--- a/.github/helper/semgrep_rules/security.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def function_name(input):
-	# ruleid: frappe-codeinjection-eval
-	eval(input)
-
-# ok: frappe-codeinjection-eval
-eval("1 + 1")
diff --git a/.github/helper/semgrep_rules/security.yml b/.github/helper/semgrep_rules/security.yml
deleted file mode 100644
index 8b21979..0000000
--- a/.github/helper/semgrep_rules/security.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-rules:
-- id: frappe-codeinjection-eval
-  patterns:
-  - pattern-not: eval("...")
-  - pattern: eval(...)
-  message: |
-    Detected the use of eval(). eval() can be dangerous if used to evaluate
-    dynamic content. Avoid it or use safe_eval().
-  languages: [python]
-  severity: ERROR
diff --git a/.github/helper/semgrep_rules/translate.js b/.github/helper/semgrep_rules/translate.js
deleted file mode 100644
index 9cdfb75..0000000
--- a/.github/helper/semgrep_rules/translate.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// ruleid: frappe-translation-empty-string
-__("")
-// ruleid: frappe-translation-empty-string
-__('')
-
-// ok: frappe-translation-js-formatting
-__('Welcome {0}, get started with ERPNext in just a few clicks.', [full_name]);
-
-// ruleid: frappe-translation-js-formatting
-__(`Welcome ${full_name}, get started with ERPNext in just a few clicks.`);
-
-// ok: frappe-translation-js-formatting
-__('This is fine');
-
-
-// ok: frappe-translation-trailing-spaces
-__('This is fine');
-
-// ruleid: frappe-translation-trailing-spaces
-__(' this is not ok ');
-// ruleid: frappe-translation-trailing-spaces
-__('this is not ok ');
-// ruleid: frappe-translation-trailing-spaces
-__(' this is not ok');
-
-// ok: frappe-translation-js-splitting
-__('You have {0} subscribers in your mailing list.', [subscribers.length])
-
-// todoruleid: frappe-translation-js-splitting
-__('You have') + subscribers.length + __('subscribers in your mailing list.')
-
-// ruleid: frappe-translation-js-splitting
-__('You have' + 'subscribers in your mailing list.')
-
-// ruleid: frappe-translation-js-splitting
-__('You have {0} subscribers' +
-    'in your mailing list', [subscribers.length])
-
-// ok: frappe-translation-js-splitting
-__("Ctrl+Enter to add comment")
-
-// ruleid: frappe-translation-js-splitting
-__('You have {0} subscribers \
-    in your mailing list', [subscribers.length])
diff --git a/.github/helper/semgrep_rules/translate.py b/.github/helper/semgrep_rules/translate.py
deleted file mode 100644
index 9de6aa9..0000000
--- a/.github/helper/semgrep_rules/translate.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Examples taken from https://frappeframework.com/docs/user/en/translations
-# This file is used for testing the tests.
-
-from frappe import _
-
-full_name = "Jon Doe"
-# ok: frappe-translation-python-formatting
-_('Welcome {0}, get started with ERPNext in just a few clicks.').format(full_name)
-
-# ruleid: frappe-translation-python-formatting
-_('Welcome %s, get started with ERPNext in just a few clicks.' % full_name)
-# ruleid: frappe-translation-python-formatting
-_('Welcome %(name)s, get started with ERPNext in just a few clicks.' % {'name': full_name})
-
-# ruleid: frappe-translation-python-formatting
-_('Welcome {0}, get started with ERPNext in just a few clicks.'.format(full_name))
-
-
-subscribers = ["Jon", "Doe"]
-# ok: frappe-translation-python-formatting
-_('You have {0} subscribers in your mailing list.').format(len(subscribers))
-
-# ruleid: frappe-translation-python-splitting
-_('You have') + len(subscribers) + _('subscribers in your mailing list.')
-
-# ruleid: frappe-translation-python-splitting
-_('You have {0} subscribers \
-    in your mailing list').format(len(subscribers))
-
-# ok: frappe-translation-python-splitting
-_('You have {0} subscribers') \
-    + 'in your mailing list'
-
-# ruleid: frappe-translation-trailing-spaces
-msg = _(" You have {0} pending invoice ")
-# ruleid: frappe-translation-trailing-spaces
-msg = _("You have {0} pending invoice ")
-# ruleid: frappe-translation-trailing-spaces
-msg = _(" You have {0} pending invoice")
-
-# ok: frappe-translation-trailing-spaces
-msg = ' ' + _("You have {0} pending invoices") + ' '
-
-# ruleid: frappe-translation-python-formatting
-_(f"can not format like this - {subscribers}")
-# ruleid: frappe-translation-python-splitting
-_(f"what" + f"this is also not cool")
-
-
-# ruleid: frappe-translation-empty-string
-_("")
-# ruleid: frappe-translation-empty-string
-_('')
-
-
-class Test:
-	# ok: frappe-translation-python-splitting
-	def __init__(
-			args
-			):
-		pass
diff --git a/.github/helper/semgrep_rules/translate.yml b/.github/helper/semgrep_rules/translate.yml
deleted file mode 100644
index 5f03fb9..0000000
--- a/.github/helper/semgrep_rules/translate.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-rules:
-- id: frappe-translation-empty-string
-  pattern-either:
-  - pattern: _("")
-  - pattern: __("")
-  message: |
-    Empty string is useless for translation.
-    Please refer: https://frappeframework.com/docs/user/en/translations
-  languages: [python, javascript, json]
-  severity: ERROR
-
-- id: frappe-translation-trailing-spaces
-  pattern-either:
-    - pattern: _("=~/(^[ \t]+|[ \t]+$)/")
-    - pattern: __("=~/(^[ \t]+|[ \t]+$)/")
-  message: |
-    Trailing or leading whitespace not allowed in translate strings.
-    Please refer: https://frappeframework.com/docs/user/en/translations
-  languages: [python, javascript, json]
-  severity: ERROR
-
-- id: frappe-translation-python-formatting
-  pattern-either:
-  - pattern: _("..." % ...)
-  - pattern: _("...".format(...))
-  - pattern: _(f"...")
-  message: |
-    Only positional formatters are allowed and formatting should not be done before translating.
-    Please refer: https://frappeframework.com/docs/user/en/translations
-  languages: [python]
-  severity: ERROR
-
-- id: frappe-translation-js-formatting
-  patterns:
-  - pattern: __(`...`)
-  - pattern-not: __("...")
-  message: |
-    Template strings are not allowed for text formatting.
-    Please refer: https://frappeframework.com/docs/user/en/translations
-  languages: [javascript, json]
-  severity: ERROR
-
-- id: frappe-translation-python-splitting
-  pattern-either:
-  - pattern: _(...) + _(...)
-  - pattern: _("..." + "...")
-  - pattern-regex: '[\s\.]_\([^\)]*\\\s*'    # lines broken by `\`
-  - pattern-regex: '[\s\.]_\(\s*\n'          # line breaks allowed by python for using ( )
-  message: |
-    Do not split strings inside translate function. Do not concatenate using translate functions.
-    Please refer: https://frappeframework.com/docs/user/en/translations
-  languages: [python]
-  severity: ERROR
-
-- id: frappe-translation-js-splitting
-  pattern-either:
-  - pattern-regex: '__\([^\)]*[\\]\s+'
-  - pattern: __('...' + '...', ...)
-  - pattern: __('...') + __('...')
-  message: |
-    Do not split strings inside translate function. Do not concatenate using translate functions.
-    Please refer: https://frappeframework.com/docs/user/en/translations
-  languages: [javascript, json]
-  severity: ERROR
diff --git a/.github/helper/semgrep_rules/ux.js b/.github/helper/semgrep_rules/ux.js
deleted file mode 100644
index ae73f9c..0000000
--- a/.github/helper/semgrep_rules/ux.js
+++ /dev/null
@@ -1,9 +0,0 @@
-
-// ok: frappe-missing-translate-function-js
-frappe.msgprint('{{ _("Both login and password required") }}');
-
-// ruleid: frappe-missing-translate-function-js
-frappe.msgprint('What');
-
-// ok: frappe-missing-translate-function-js
-frappe.throw('  {{ _("Both login and password required") }}.  ');
diff --git a/.github/helper/semgrep_rules/ux.py b/.github/helper/semgrep_rules/ux.py
deleted file mode 100644
index a00d3cd..0000000
--- a/.github/helper/semgrep_rules/ux.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import frappe
-from frappe import msgprint, throw, _
-
-
-# ruleid: frappe-missing-translate-function-python
-throw("Error Occured")
-
-# ruleid: frappe-missing-translate-function-python
-frappe.throw("Error Occured")
-
-# ruleid: frappe-missing-translate-function-python
-frappe.msgprint("Useful message")
-
-# ruleid: frappe-missing-translate-function-python
-msgprint("Useful message")
-
-
-# ok: frappe-missing-translate-function-python
-translatedmessage = _("Hello")
-
-# ok: frappe-missing-translate-function-python
-throw(translatedmessage)
-
-# ok: frappe-missing-translate-function-python
-msgprint(translatedmessage)
-
-# ok: frappe-missing-translate-function-python
-msgprint(_("Helpful message"))
-
-# ok: frappe-missing-translate-function-python
-frappe.throw(_("Error occured"))
diff --git a/.github/helper/semgrep_rules/ux.yml b/.github/helper/semgrep_rules/ux.yml
deleted file mode 100644
index dd667f3..0000000
--- a/.github/helper/semgrep_rules/ux.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-rules:
-- id: frappe-missing-translate-function-python
-  pattern-either:
-  - patterns:
-      - pattern: frappe.msgprint("...", ...)
-      - pattern-not: frappe.msgprint(_("..."), ...)
-  - patterns:
-      - pattern: frappe.throw("...", ...)
-      - pattern-not: frappe.throw(_("..."), ...)
-  message: |
-      All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
-  languages: [python]
-  severity: ERROR
-
-- id: frappe-missing-translate-function-js
-  pattern-either:
-  - patterns:
-      - pattern: frappe.msgprint("...", ...)
-      - pattern-not: frappe.msgprint(__("..."), ...)
-      # ignore microtemplating e.g. msgprint("{{ _("server side translation") }}")
-      - pattern-not: frappe.msgprint("=~/\{\{.*\_.*\}\}/i", ...)
-  - patterns:
-      - pattern: frappe.throw("...", ...)
-      - pattern-not: frappe.throw(__("..."), ...)
-      # ignore microtemplating
-      - pattern-not: frappe.throw("=~/\{\{.*\_.*\}\}/i", ...)
-  message: |
-      All user facing text must be wrapped in translate function. Please refer to translation documentation. https://frappeframework.com/docs/user/en/guides/basics/translations
-  languages: [javascript]
-  severity: ERROR
diff --git a/.github/try-on-f-cloud-button.svg b/.github/try-on-f-cloud-button.svg
new file mode 100644
index 0000000..fe0bb2c
--- /dev/null
+++ b/.github/try-on-f-cloud-button.svg
@@ -0,0 +1,32 @@
+<svg width="201" height="60" viewBox="0 0 201 60" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g filter="url(#filter0_dd)">
+<rect x="4" y="2" width="193" height="52" rx="6" fill="#2490EF"/>
+<path d="M28 22.2891H32.8786V35.5H36.2088V22.2891H41.0874V19.5H28V22.2891Z" fill="white"/>
+<path d="M41.6982 35.5H45.0129V28.7109C45.0129 27.2344 46.0866 26.2188 47.5494 26.2188C48.0085 26.2188 48.6388 26.2969 48.95 26.3984V23.4453C48.6543 23.375 48.2419 23.3281 47.9074 23.3281C46.5691 23.3281 45.472 24.1094 45.0362 25.5938H44.9117V23.5H41.6982V35.5Z" fill="white"/>
+<path d="M52.8331 40C55.2996 40 56.6068 38.7344 57.2837 36.7969L61.9289 23.5156L58.4197 23.5L55.9221 32.3125H55.7976L53.3233 23.5H49.8374L54.1247 35.8437L53.9302 36.3516C53.4944 37.4766 52.6619 37.5312 51.4947 37.1719L50.7478 39.6562C51.2224 39.8594 51.9927 40 52.8331 40Z" fill="white"/>
+<path d="M73.6142 35.7344C77.2401 35.7344 79.4966 33.2422 79.4966 29.5469C79.4966 25.8281 77.2401 23.3438 73.6142 23.3438C69.9883 23.3438 67.7319 25.8281 67.7319 29.5469C67.7319 33.2422 69.9883 35.7344 73.6142 35.7344ZM73.6298 33.1562C71.9569 33.1562 71.101 31.6171 71.101 29.5233C71.101 27.4296 71.9569 25.8827 73.6298 25.8827C75.2715 25.8827 76.1274 27.4296 76.1274 29.5233C76.1274 31.6171 75.2715 33.1562 73.6298 33.1562Z" fill="white"/>
+<path d="M84.7253 28.5625C84.7331 27.0156 85.6512 26.1094 86.9895 26.1094C88.3201 26.1094 89.1215 26.9844 89.1137 28.4531V35.5H92.4284V27.8594C92.4284 25.0625 90.7945 23.3438 88.3046 23.3438C86.5306 23.3438 85.2466 24.2187 84.7097 25.6172H84.5697V23.5H81.4106V35.5H84.7253V28.5625Z" fill="white"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M102.429 19.5H113.429V22.3141H102.429V19.5ZM102.429 35.5V26.6794H112.699V29.4982H105.94V35.5H102.429Z" fill="white"/>
+<path d="M131.584 24.9625C131.09 21.5057 128.345 19.5 124.785 19.5C120.589 19.5 117.429 22.463 117.429 27.4924C117.429 32.5142 120.55 35.4848 124.785 35.4848C128.604 35.4848 131.137 33.0916 131.584 30.1211L128.651 30.1059C128.282 31.9293 126.745 32.9549 124.824 32.9549C122.22 32.9549 120.354 31.0632 120.354 27.4924C120.354 23.9824 122.204 22.0299 124.832 22.0299C126.784 22.0299 128.314 23.1011 128.651 24.9625H131.584Z" fill="white"/>
+<path d="M136.409 19.7124H133.571V35.2718H136.409V19.7124Z" fill="white"/>
+<path d="M144.031 35.5001C147.56 35.5001 149.803 33.0917 149.803 29.483C149.803 25.8667 147.56 23.4507 144.031 23.4507C140.502 23.4507 138.259 25.8667 138.259 29.483C138.259 33.0917 140.502 35.5001 144.031 35.5001ZM144.047 33.2969C142.094 33.2969 141.137 31.6103 141.137 29.4754C141.137 27.3406 142.094 25.6312 144.047 25.6312C145.968 25.6312 146.925 27.3406 146.925 29.4754C146.925 31.6103 145.968 33.2969 144.047 33.2969Z" fill="white"/>
+<path d="M159.338 30.3641C159.338 32.1419 158.028 33.0232 156.773 33.0232C155.409 33.0232 154.499 32.0887 154.499 30.6072V23.6025H151.66V31.0327C151.66 33.8361 153.307 35.4239 155.675 35.4239C157.479 35.4239 158.749 34.5046 159.298 33.1979H159.424V35.272H162.176V23.6025H159.338V30.3641Z" fill="white"/>
+<path d="M169.014 35.4769C171.084 35.4769 172.017 34.2841 172.464 33.4332H172.637V35.2718H175.429V19.7124H172.582V25.532H172.464C172.033 24.6887 171.147 23.4503 169.022 23.4503C166.238 23.4503 164.05 25.5624 164.05 29.4522C164.05 33.2965 166.175 35.4769 169.014 35.4769ZM169.806 33.2205C167.931 33.2205 166.943 31.6251 166.943 29.437C166.943 27.2642 167.916 25.7067 169.806 25.7067C171.633 25.7067 172.637 27.173 172.637 29.437C172.637 31.701 171.617 33.2205 169.806 33.2205Z" fill="white"/>
+</g>
+<defs>
+<filter id="filter0_dd" x="0" y="0" width="201" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+<feFlood flood-opacity="0" result="BackgroundImageFix"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset/>
+<feGaussianBlur stdDeviation="0.25"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
+<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
+<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
+<feOffset dy="2"/>
+<feGaussianBlur stdDeviation="2"/>
+<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
+<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
+<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
+</filter>
+</defs>
+</svg>
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index 16e490a..ebb88c9 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -10,13 +10,6 @@
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - uses: returntocorp/semgrep-action@v1
-        env:
-            SEMGREP_TIMEOUT: 120
-        with:
-            config: >-
-              r/python.lang.correctness
-              .github/helper/semgrep_rules
 
       - name: Set up Python 3.8
         uses: actions/setup-python@v2
@@ -25,3 +18,14 @@
 
       - name: Install and Run Pre-commit
         uses: pre-commit/action@v2.0.3
+
+      - name: Download Semgrep rules
+        run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
+
+      - uses: returntocorp/semgrep-action@v1
+        env:
+            SEMGREP_TIMEOUT: 120
+        with:
+            config: >-
+              r/python.lang.correctness
+              ./frappe-semgrep-rules/rules
diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml
index 4f84b86..77c0aee 100644
--- a/.github/workflows/server-tests.yml
+++ b/.github/workflows/server-tests.yml
@@ -91,6 +91,8 @@
 
       - name: Install
         run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
+        env:
+          TYPE: server
 
       - name: Run Tests
         run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
index 658892c..d765f04 100644
--- a/.github/workflows/ui-tests.yml
+++ b/.github/workflows/ui-tests.yml
@@ -104,6 +104,8 @@
 
       - name: Build Assets
         run: cd ~/frappe-bench/ && bench build
+        env:
+          CI: Yes
 
       - name: UI Tests
         run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 2b3a471..b74d9a6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -20,6 +20,9 @@
     rev: 3.9.2
     hooks:
       - id: flake8
+        additional_dependencies: [
+          'flake8-bugbear',
+        ]
         args: ['--config', '.github/helper/.flake8_strict']
         exclude: ".*setup.py$"
 
diff --git a/README.md b/README.md
index 847904d..1105a97 100644
--- a/README.md
+++ b/README.md
@@ -39,6 +39,12 @@
 
 ---
 
+<div align="center">
+    <a href="https://frappecloud.com/deploy?apps=frappe,erpnext&source=erpnext_readme">
+        <img src=".github/try-on-f-cloud-button.svg" height="40">
+    </a>
+</div>
+
 ### Containerized Installation
 
 Use docker to deploy ERPNext in production or for development of [Frappe](https://github.com/frappe/frappe) apps. See https://github.com/frappe/frappe_docker for more details.
@@ -49,14 +55,6 @@
 
 New passwords will be created for the ERPNext "Administrator" user, the MariaDB root user, and the frappe user (the script displays the passwords and saves them to ~/frappe_passwords.txt).
 
-### Virtual Image
-
-You can download a virtual image to run ERPNext in a virtual machine on your local system.
-
-- [ERPNext Download](http://erpnext.com/download)
-
-System and user credentials are listed on the download page.
-
 ---
 
 ## License
@@ -77,6 +75,12 @@
 
 ---
 
+## Learning
+
+1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
+
+---
+
 ## Logo and Trademark
 
 The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js
index 79e08b3..464cce4 100644
--- a/cypress/integration/test_organizational_chart_desktop.js
+++ b/cypress/integration/test_organizational_chart_desktop.js
@@ -24,7 +24,7 @@
 				cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
 				cy.get('@input')
 					.clear({ force: true })
-					.type('Test Org Chart{enter}', { force: true })
+					.type('Test Org Chart{downarrow}{enter}', { force: true })
 					.blur({ force: true });
 			});
 		});
diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js
index 161fae0..971ac6d 100644
--- a/cypress/integration/test_organizational_chart_mobile.js
+++ b/cypress/integration/test_organizational_chart_mobile.js
@@ -25,7 +25,7 @@
 				cy.get('.frappe-control[data-fieldname=company] input').focus().as('input');
 				cy.get('@input')
 					.clear({ force: true })
-					.type('Test Org Chart{enter}', { force: true })
+					.type('Test Org Chart{downarrow}{enter}', { force: true })
 					.blur({ force: true });
 			});
 		});
diff --git a/erpnext/accounts/custom/address.py b/erpnext/accounts/custom/address.py
index a6d08d8..551048e 100644
--- a/erpnext/accounts/custom/address.py
+++ b/erpnext/accounts/custom/address.py
@@ -10,6 +10,7 @@
 class ERPNextAddress(Address):
 	def validate(self):
 		self.validate_reference()
+		self.update_compnay_address()
 		super(ERPNextAddress, self).validate()
 
 	def link_address(self):
@@ -19,6 +20,11 @@
 
 		return super(ERPNextAddress, self).link_address()
 
+	def update_compnay_address(self):
+		for link in self.get('links'):
+			if link.link_doctype == 'Company':
+				self.is_your_company_address = 1
+
 	def validate_reference(self):
 		if self.is_your_company_address and not [
 			row for row in self.links if row.link_doctype == "Company"
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index f6198eb..605262f 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -8,6 +8,8 @@
 from frappe.utils import cint, cstr
 from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of
 
+import erpnext
+
 
 class RootNotEditable(frappe.ValidationError): pass
 class BalanceMismatchError(frappe.ValidationError): pass
@@ -196,7 +198,7 @@
 					"company": company,
 					# parent account's currency should be passed down to child account's curreny
 					# if it is None, it picks it up from default company currency, which might be unintended
-					"account_currency": self.account_currency,
+					"account_currency": erpnext.get_company_currency(company),
 					"parent_account": parent_acc_name_map[company]
 				})
 
@@ -207,8 +209,7 @@
 				# update the parent company's value in child companies
 				doc = frappe.get_doc("Account", child_account)
 				parent_value_changed = False
-				for field in ['account_type', 'account_currency',
-					'freeze_account', 'balance_must_be']:
+				for field in ['account_type', 'freeze_account', 'balance_must_be']:
 					if doc.get(field) != self.get(field):
 						parent_value_changed = True
 						doc.set(field, self.get(field))
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
index d6ccd16..05caafe 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/chart_of_accounts.py
@@ -12,7 +12,7 @@
 from unidecode import unidecode
 
 
-def create_charts(company, chart_template=None, existing_company=None, custom_chart=None):
+def create_charts(company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None):
 	chart = custom_chart or get_chart(chart_template, existing_company)
 	if chart:
 		accounts = []
@@ -22,7 +22,7 @@
 				if root_account:
 					root_type = child.get("root_type")
 
-				if account_name not in ["account_number", "account_type",
+				if account_name not in ["account_name", "account_number", "account_type",
 					"root_type", "is_group", "tax_rate"]:
 
 					account_number = cstr(child.get("account_number")).strip()
@@ -35,7 +35,7 @@
 
 					account = frappe.get_doc({
 						"doctype": "Account",
-						"account_name": account_name,
+						"account_name": child.get('account_name') if from_coa_importer else account_name,
 						"company": company,
 						"parent_account": parent,
 						"is_group": is_group,
@@ -213,7 +213,7 @@
 	return (bank_account in accounts)
 
 @frappe.whitelist()
-def build_tree_from_json(chart_template, chart_data=None):
+def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
 	''' get chart template from its folder and parse the json to be rendered as tree '''
 	chart = chart_data or get_chart(chart_template)
 
@@ -226,9 +226,12 @@
 		''' recursively called to form a parent-child based list of dict from chart template '''
 		for account_name, child in iteritems(children):
 			account = {}
-			if account_name in ["account_number", "account_type",\
+			if account_name in ["account_name", "account_number", "account_type",\
 				"root_type", "is_group", "tax_rate"]: continue
 
+			if from_coa_importer:
+				account_name = child['account_name']
+
 			account['parent_account'] = parent
 			account['expandable'] = True if identify_is_group(child) else False
 			account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 7d0ecfb..55ea571 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -174,7 +174,7 @@
    "default": "0",
    "fieldname": "automatically_fetch_payment_terms",
    "fieldtype": "Check",
-   "label": "Automatically Fetch Payment Terms"
+   "label": "Automatically Fetch Payment Terms from Order"
   },
   {
    "description": "The percentage you are allowed to bill more against the amount ordered. For example, if the order value is $100 for an item and tolerance is set as 10%, then you are allowed to bill up to $110 ",
@@ -282,7 +282,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-08-19 11:17:38.788054",
+ "modified": "2021-10-11 17:42:36.427699",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
index 66a269e..d61f8a6 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
@@ -10,6 +10,15 @@
 		// make company mandatory
 		frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1);
 		frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
+
+		if (frm.doc.import_file) {
+			frappe.run_serially([
+				() => generate_tree_preview(frm),
+				() => create_import_button(frm),
+				() => frm.set_df_property('chart_preview', 'hidden', 0)
+			]);
+		}
+
 		frm.set_df_property('chart_preview', 'hidden',
 			$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
 	},
@@ -72,13 +81,6 @@
 		if (!frm.doc.import_file) {
 			frm.page.set_indicator("");
 			$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
-		} else {
-			frappe.run_serially([
-				() => validate_coa(frm),
-				() => generate_tree_preview(frm),
-				() => create_import_button(frm),
-				() => frm.set_df_property('chart_preview', 'hidden', 0),
-			]);
 		}
 	},
 
@@ -104,26 +106,24 @@
 });
 
 var create_import_button = function(frm) {
-	if (frm.page.show_import_button) {
-		frm.page.set_primary_action(__("Import"), function () {
-			return frappe.call({
-				method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
-				args: {
-					file_name: frm.doc.import_file,
-					company: frm.doc.company
-				},
-				freeze: true,
-				freeze_message: __("Creating Accounts..."),
-				callback: function(r) {
-					if (!r.exc) {
-						clearInterval(frm.page["interval"]);
-						frm.page.set_indicator(__('Import Successful'), 'blue');
-						create_reset_button(frm);
-					}
+	frm.page.set_primary_action(__("Import"), function () {
+		return frappe.call({
+			method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
+			args: {
+				file_name: frm.doc.import_file,
+				company: frm.doc.company
+			},
+			freeze: true,
+			freeze_message: __("Creating Accounts..."),
+			callback: function(r) {
+				if (!r.exc) {
+					clearInterval(frm.page["interval"]);
+					frm.page.set_indicator(__('Import Successful'), 'blue');
+					create_reset_button(frm);
 				}
-			});
-		}).addClass('btn btn-primary');
-	}
+			}
+		});
+	}).addClass('btn btn-primary');
 };
 
 var create_reset_button = function(frm) {
@@ -137,7 +137,6 @@
 var validate_coa = function(frm) {
 	if (frm.doc.import_file) {
 		let parent = __('All Accounts');
-
 		return frappe.call({
 			'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
 			'args': {
@@ -157,25 +156,23 @@
 };
 
 var generate_tree_preview = function(frm) {
-	if (frm.doc.import_file) {
-		let parent = __('All Accounts');
-		$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
+	let parent = __('All Accounts');
+	$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
 
-		// generate tree structure based on the csv data
-		return new frappe.ui.Tree({
-			parent: $(frm.fields_dict['chart_tree'].wrapper),
-			label: parent,
-			expandable: true,
-			method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
-			args: {
-				file_name: frm.doc.import_file,
-				parent: parent,
-				doctype: 'Chart of Accounts Importer',
-				file_type: frm.doc.file_type
-			},
-			onclick: function(node) {
-				parent = node.value;
-			}
-		});
-	}
+	// generate tree structure based on the csv data
+	return new frappe.ui.Tree({
+		parent: $(frm.fields_dict['chart_tree'].wrapper),
+		label: parent,
+		expandable: true,
+		method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
+		args: {
+			file_name: frm.doc.import_file,
+			parent: parent,
+			doctype: 'Chart of Accounts Importer',
+			file_type: frm.doc.file_type
+		},
+		onclick: function(node) {
+			parent = node.value;
+		}
+	});
 };
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index bd2a6f1..eabe408 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -25,7 +25,9 @@
 
 
 class ChartofAccountsImporter(Document):
-	pass
+	def validate(self):
+		if self.import_file:
+			get_coa('Chart of Accounts Importer', 'All Accounts', file_name=self.import_file, for_validate=1)
 
 def validate_columns(data):
 	if not data:
@@ -34,7 +36,8 @@
 	no_of_columns = max([len(d) for d in data])
 
 	if no_of_columns > 7:
-		frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'))
+		frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'),
+			title=(_("Wrong Template")))
 
 @frappe.whitelist()
 def validate_company(company):
@@ -66,7 +69,7 @@
 
 	frappe.local.flags.ignore_root_company_validation = True
 	forest = build_forest(data)
-	create_charts(company, custom_chart=forest)
+	create_charts(company, custom_chart=forest, from_coa_importer=True)
 
 	# trigger on_update for company to reset default accounts
 	set_default_accounts(company)
@@ -145,7 +148,7 @@
 
 	if not for_validate:
 		forest = build_forest(data)
-		accounts = build_tree_from_json("", chart_data=forest) # returns a list of dict in a tree render-able form
+		accounts = build_tree_from_json("", chart_data=forest, from_coa_importer=True) # returns a list of dict in a tree render-able form
 
 		# filter out to show data for the selected node only
 		accounts = [d for d in accounts if d['parent_account']==parent]
@@ -209,11 +212,14 @@
 		if not account_name:
 			error_messages.append("Row {0}: Please enter Account Name".format(line_no))
 
+		name = account_name
 		if account_number:
 			account_number = cstr(account_number).strip()
 			account_name = "{} - {}".format(account_number, account_name)
 
 		charts_map[account_name] = {}
+		charts_map[account_name]['account_name'] = name
+		if account_number: charts_map[account_name]["account_number"] = account_number
 		if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
 		if account_type: charts_map[account_name]["account_type"] = account_type
 		if root_type: charts_map[account_name]["root_type"] = root_type
diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
index 0813926..003389e 100644
--- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
+++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.py
@@ -16,7 +16,7 @@
 
 def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=None):
 	if not expiry_date:
-		date = today()
+		expiry_date = today()
 
 	return frappe.db.sql('''
 		select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index 6f362c1..ee2e319 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -27,10 +27,12 @@
   "payment_accounts_section",
   "party_balance",
   "paid_from",
+  "paid_from_account_type",
   "paid_from_account_currency",
   "paid_from_account_balance",
   "column_break_18",
   "paid_to",
+  "paid_to_account_type",
   "paid_to_account_currency",
   "paid_to_account_balance",
   "payment_amounts_section",
@@ -440,7 +442,8 @@
    "depends_on": "eval:(doc.paid_from && doc.paid_to)",
    "fieldname": "reference_no",
    "fieldtype": "Data",
-   "label": "Cheque/Reference No"
+   "label": "Cheque/Reference No",
+   "mandatory_depends_on": "eval:(doc.paid_from_account_type == 'Bank' || doc.paid_to_account_type == 'Bank')"
   },
   {
    "fieldname": "column_break_23",
@@ -452,6 +455,7 @@
    "fieldname": "reference_date",
    "fieldtype": "Date",
    "label": "Cheque/Reference Date",
+   "mandatory_depends_on": "eval:(doc.paid_from_account_type == 'Bank' || doc.paid_to_account_type == 'Bank')",
    "search_index": 1
   },
   {
@@ -707,15 +711,30 @@
    "label": "Received Amount After Tax (Company Currency)",
    "options": "Company:company:default_currency",
    "read_only": 1
+  },
+  {
+   "fetch_from": "paid_from.account_type",
+   "fieldname": "paid_from_account_type",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Paid From Account Type"
+  },
+  {
+   "fetch_from": "paid_to.account_type",
+   "fieldname": "paid_to_account_type",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Paid To Account Type"
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-07-09 08:58:15.008761",
+ "modified": "2021-10-22 17:50:24.632806",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Entry",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 8037ca1..9b4a91d 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -505,12 +505,13 @@
 
 	def validate_received_amount(self):
 		if self.paid_from_account_currency == self.paid_to_account_currency:
-			if self.paid_amount != self.received_amount:
+			if self.paid_amount < self.received_amount:
 				frappe.throw(_("Received Amount cannot be greater than Paid Amount"))
 
 	def set_received_amount(self):
 		self.base_received_amount = self.base_paid_amount
-		if self.paid_from_account_currency == self.paid_to_account_currency:
+		if self.paid_from_account_currency == self.paid_to_account_currency \
+			and not self.payment_type == 'Internal Transfer':
 			self.received_amount = self.paid_amount
 
 	def set_amounts_after_tax(self):
@@ -712,10 +713,14 @@
 			dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
 
 			for d in self.get("references"):
+				cost_center = self.cost_center
+				if d.reference_doctype == "Sales Invoice" and not cost_center:
+					cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
 				gle = party_gl_dict.copy()
 				gle.update({
 					"against_voucher_type": d.reference_doctype,
-					"against_voucher": d.reference_name
+					"against_voucher": d.reference_name,
+					"cost_center": cost_center
 				})
 
 				allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js
index aa373bc..9074def 100644
--- a/erpnext/accounts/doctype/payment_order/payment_order.js
+++ b/erpnext/accounts/doctype/payment_order/payment_order.js
@@ -10,6 +10,9 @@
 				}
 			}
 		});
+
+		frm.set_df_property('references', 'cannot_add_rows', true);
+		frm.set_df_property('references', 'cannot_delete_rows', true);
 	},
 	refresh: function(frm) {
 		if (frm.doc.docstatus == 0) {
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
index b1f3e6f..412833b 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
@@ -52,21 +52,35 @@
 
 	refresh() {
 		this.frm.disable_save();
+		this.frm.set_df_property('invoices', 'cannot_delete_rows', true);
+		this.frm.set_df_property('payments', 'cannot_delete_rows', true);
+		this.frm.set_df_property('allocation', 'cannot_delete_rows', true);
+
+		this.frm.set_df_property('invoices', 'cannot_add_rows', true);
+		this.frm.set_df_property('payments', 'cannot_add_rows', true);
+		this.frm.set_df_property('allocation', 'cannot_add_rows', true);
+
 
 		if (this.frm.doc.receivable_payable_account) {
 			this.frm.add_custom_button(__('Get Unreconciled Entries'), () =>
 				this.frm.trigger("get_unreconciled_entries")
 			);
+			this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary');
 		}
 		if (this.frm.doc.invoices.length && this.frm.doc.payments.length) {
 			this.frm.add_custom_button(__('Allocate'), () =>
 				this.frm.trigger("allocate")
 			);
+			this.frm.change_custom_button_type('Allocate', null, 'primary');
+			this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default');
 		}
 		if (this.frm.doc.allocation.length) {
 			this.frm.add_custom_button(__('Reconcile'), () =>
 				this.frm.trigger("reconcile")
 			);
+			this.frm.change_custom_button_type('Reconcile', null, 'primary');
+			this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default');
+			this.frm.change_custom_button_type('Allocate', null, 'default');
 		}
 	}
 
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
index 9023b36..eb0c20f 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
@@ -12,15 +12,16 @@
   "receivable_payable_account",
   "col_break1",
   "from_invoice_date",
-  "to_invoice_date",
-  "minimum_invoice_amount",
-  "maximum_invoice_amount",
-  "invoice_limit",
-  "column_break_13",
   "from_payment_date",
-  "to_payment_date",
+  "minimum_invoice_amount",
   "minimum_payment_amount",
+  "column_break_11",
+  "to_invoice_date",
+  "to_payment_date",
+  "maximum_invoice_amount",
   "maximum_payment_amount",
+  "column_break_13",
+  "invoice_limit",
   "payment_limit",
   "bank_cash_account",
   "sec_break1",
@@ -79,6 +80,7 @@
   },
   {
    "depends_on": "eval:(doc.payments).length || (doc.invoices).length",
+   "description": "If you need to reconcile particular transactions against each other, then please select accordingly. If not, all the transactions will be allocated in FIFO order.",
    "fieldname": "sec_break1",
    "fieldtype": "Section Break",
    "label": "Unreconciled Entries"
@@ -163,6 +165,7 @@
    "label": "Maximum Payment Amount"
   },
   {
+   "description": "System will fetch all the entries if limit value is zero.",
    "fieldname": "payment_limit",
    "fieldtype": "Int",
    "label": "Payment Limit"
@@ -171,13 +174,17 @@
    "fieldname": "maximum_invoice_amount",
    "fieldtype": "Currency",
    "label": "Maximum Invoice Amount"
+  },
+  {
+   "fieldname": "column_break_11",
+   "fieldtype": "Column Break"
   }
  ],
  "hide_toolbar": 1,
  "icon": "icon-resize-horizontal",
  "issingle": 1,
  "links": [],
- "modified": "2021-08-30 13:05:51.977861",
+ "modified": "2021-10-04 20:27:11.114194",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation",
diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
index b8c65ee..6a21692 100644
--- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
+++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json
@@ -14,8 +14,8 @@
   "section_break_6",
   "allocated_amount",
   "unreconciled_amount",
-  "amount",
   "column_break_8",
+  "amount",
   "is_advance",
   "section_break_5",
   "difference_amount",
@@ -127,12 +127,13 @@
    "fieldname": "reference_row",
    "fieldtype": "Data",
    "hidden": 1,
-   "label": "Reference Row"
+   "label": "Reference Row",
+   "read_only": 1
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-09-20 17:23:09.455803",
+ "modified": "2021-10-06 11:48:59.616562",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Payment Reconciliation Allocation",
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
index 4d6e4a2..d6e35c6 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
@@ -180,8 +180,7 @@
    "fieldname": "pos_transactions",
    "fieldtype": "Table",
    "label": "POS Transactions",
-   "options": "POS Invoice Reference",
-   "reqd": 1
+   "options": "POS Invoice Reference"
   },
   {
    "fieldname": "pos_opening_entry",
@@ -229,7 +228,7 @@
    "link_fieldname": "pos_closing_entry"
   }
  ],
- "modified": "2021-05-05 16:59:49.723261",
+ "modified": "2021-10-20 16:19:25.340565",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Closing Entry",
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 9dae3a7..4f26ed4 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -246,7 +246,10 @@
 	return pos_invoice_customer_map
 
 def consolidate_pos_invoices(pos_invoices=None, closing_entry=None):
-	invoices = pos_invoices or (closing_entry and closing_entry.get('pos_transactions')) or get_all_unconsolidated_invoices()
+	invoices = pos_invoices or (closing_entry and closing_entry.get('pos_transactions'))
+	if frappe.flags.in_test and not invoices:
+		invoices = get_all_unconsolidated_invoices()
+
 	invoice_by_customer = get_invoice_customer_map(invoices)
 
 	if len(invoices) >= 10 and closing_entry:
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 8afa0ab..9c9f37b 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -120,6 +120,7 @@
   {
    "fieldname": "payments",
    "fieldtype": "Table",
+   "label": "Payment Methods",
    "options": "POS Payment Method",
    "reqd": 1
   },
@@ -377,7 +378,7 @@
    "link_fieldname": "pos_profile"
   }
  ],
- "modified": "2021-02-01 13:52:51.081311",
+ "modified": "2021-10-14 14:17:00.469298",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Profile",
diff --git a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
index 83ecfb4..7c53f4a 100644
--- a/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
+++ b/erpnext/accounts/doctype/pos_profile/test_pos_profile.py
@@ -33,7 +33,9 @@
 
 		frappe.db.sql("delete from `tabPOS Profile`")
 
-def get_customers_list(pos_profile={}):
+def get_customers_list(pos_profile=None):
+	if pos_profile is None:
+		pos_profile = {}
 	cond = "1=1"
 	customer_groups = []
 	if pos_profile.get('customer_groups'):
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 12b486e..ef44b41 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -29,6 +29,9 @@
 	pricing_rules = []
 	values =  {}
 
+	if not frappe.db.exists('Pricing Rule', {'disable': 0, args.transaction_type: 1}):
+		return
+
 	for apply_on in ['Item Code', 'Item Group', 'Brand']:
 		pricing_rules.extend(_get_pricing_rules(apply_on, args, values))
 		if pricing_rules and not apply_multiple_pricing_rules(pricing_rules):
@@ -398,7 +401,9 @@
 				pricing_rules[0].apply_rule_on_other_items = items
 				return pricing_rules
 
-def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
+def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
+	if items is None:
+		items = []
 	sum_qty, sum_amt = [0, 0]
 	doctype = doc.get('parenttype') or doc.doctype
 
diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
index d09f7dc..f5391ca 100644
--- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
+++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py
@@ -69,7 +69,9 @@
 			{'promotional_scheme': self.name}):
 			frappe.delete_doc('Pricing Rule', rule.name)
 
-def get_pricing_rules(doc, rules = {}):
+def get_pricing_rules(doc, rules=None):
+	if rules is None:
+		rules = {}
 	new_doc = []
 	for child_doc, fields in {'price_discount_slabs': price_discount_fields,
 		'product_discount_slabs': product_discount_fields}.items():
@@ -78,7 +80,9 @@
 
 	return new_doc
 
-def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}):
+def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
+	if rules is None:
+		rules = {}
 	new_doc = []
 	args = get_args_for_pricing_rule(doc)
 	applicable_for = frappe.scrub(doc.get('applicable_for'))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 6c74d2b..3526e48 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -590,5 +590,11 @@
 
 	company: function(frm) {
 		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+
+		if (frm.doc.company) {
+			frappe.db.get_value('Company', frm.doc.company, 'default_payable_account', (r) => {
+				frm.set_value('credit_to', r.default_payable_account);
+			});
+		}
 	},
 })
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 55e288e..03cbc4a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -149,16 +149,18 @@
   "cb_17",
   "hold_comment",
   "more_info",
+  "status",
+  "inter_company_invoice_reference",
+  "represents_company",
+  "column_break_147",
+  "is_internal_supplier",
+  "accounting_details_section",
   "credit_to",
   "party_account_currency",
   "is_opening",
   "against_expense_account",
   "column_break_63",
   "unrealized_profit_loss_account",
-  "status",
-  "inter_company_invoice_reference",
-  "is_internal_supplier",
-  "represents_company",
   "remarks",
   "subscription_section",
   "from_date",
@@ -1172,6 +1174,15 @@
    "print_hide": 1
   },
   {
+   "default": "0",
+   "fetch_from": "supplier.is_internal_supplier",
+   "fieldname": "is_internal_supplier",
+   "fieldtype": "Check",
+   "ignore_user_permissions": 1,
+   "label": "Is Internal Supplier",
+   "read_only": 1
+  },
+  {
    "fieldname": "credit_to",
    "fieldtype": "Link",
    "label": "Credit To",
@@ -1196,7 +1207,7 @@
    "default": "No",
    "fieldname": "is_opening",
    "fieldtype": "Select",
-   "label": "Is Opening",
+   "label": "Is Opening Entry",
    "oldfieldname": "is_opening",
    "oldfieldtype": "Select",
    "options": "No\nYes",
@@ -1299,15 +1310,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "default": "0",
-   "fetch_from": "supplier.is_internal_supplier",
-   "fieldname": "is_internal_supplier",
-   "fieldtype": "Check",
-   "ignore_user_permissions": 1,
-   "label": "Is Internal Supplier",
-   "read_only": 1
-  },
-  {
    "fieldname": "tax_withholding_category",
    "fieldtype": "Link",
    "hidden": 1,
@@ -1395,13 +1397,24 @@
    "hidden": 1,
    "label": "Ignore Default Payment Terms Template",
    "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_details_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details",
+   "print_hide": 1
+  },
+  {
+   "fieldname": "column_break_147",
+   "fieldtype": "Column Break"
   }
  ],
  "icon": "fa fa-file-text",
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-28 13:10:28.351810",
+ "modified": "2021-10-12 20:55:16.145651",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 1c9943f..508f728 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -15,6 +15,7 @@
 from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
 from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
 	check_if_return_invoice_linked_with_payment_entry,
+	get_total_in_party_account_currency,
 	is_overdue,
 	unlink_inter_company_doc,
 	update_linked_doc,
@@ -1183,6 +1184,7 @@
 			return
 
 		outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
+		total = get_total_in_party_account_currency(self)
 
 		if not status:
 			if self.docstatus == 2:
@@ -1190,9 +1192,9 @@
 			elif self.docstatus == 1:
 				if self.is_internal_transfer():
 					self.status = 'Internal Transfer'
-				elif is_overdue(self):
+				elif is_overdue(self, total):
 					self.status = "Overdue"
-				elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
+				elif 0 < outstanding_amount < total:
 					self.status = "Partly Paid"
 				elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
 					self.status = "Unpaid"
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 73e1284..bee153b 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -12,6 +12,13 @@
 	}
 	company() {
 		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
+
+		let me = this;
+		if (this.frm.doc.company) {
+			frappe.db.get_value('Company', this.frm.doc.company, 'default_receivable_account', (r) => {
+				me.frm.set_value('debit_to', r.default_receivable_account);
+			});
+		}
 	}
 	onload() {
 		var me = this;
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index f3adb89..93e32f1 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -124,6 +124,13 @@
   "total_advance",
   "outstanding_amount",
   "disable_rounded_total",
+  "column_break4",
+  "write_off_amount",
+  "base_write_off_amount",
+  "write_off_outstanding_amount_automatically",
+  "column_break_74",
+  "write_off_account",
+  "write_off_cost_center",
   "advances_section",
   "allocate_advances_automatically",
   "get_advances",
@@ -144,13 +151,6 @@
   "column_break_90",
   "change_amount",
   "account_for_change_amount",
-  "column_break4",
-  "write_off_amount",
-  "base_write_off_amount",
-  "write_off_outstanding_amount_automatically",
-  "column_break_74",
-  "write_off_account",
-  "write_off_cost_center",
   "terms_section_break",
   "tc_name",
   "terms",
@@ -161,14 +161,14 @@
   "column_break_84",
   "language",
   "more_information",
+  "status",
   "inter_company_invoice_reference",
-  "is_internal_customer",
   "represents_company",
   "customer_group",
   "campaign",
-  "is_discounted",
   "col_break23",
-  "status",
+  "is_internal_customer",
+  "is_discounted",
   "source",
   "more_info",
   "debit_to",
@@ -2031,7 +2031,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2021-10-02 03:36:10.251715",
+ "modified": "2021-10-11 20:19:38.667508",
  "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 eb26aa2..9190124 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -232,9 +232,6 @@
 		if self.update_stock == 1:
 			self.repost_future_sle_and_gle()
 
-		if self.update_stock == 1:
-			self.repost_future_sle_and_gle()
-
 		if not self.is_return:
 			self.update_billing_status_for_zero_amount_refdoc("Delivery Note")
 			self.update_billing_status_for_zero_amount_refdoc("Sales Order")
@@ -1287,12 +1284,20 @@
 
 			serial_nos = item.serial_no or ""
 			si_serial_nos = set(get_serial_nos(serial_nos))
+			serial_no_diff = si_serial_nos - dn_serial_nos
 
-			if si_serial_nos - dn_serial_nos:
-				frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note").format(item.idx))
+			if serial_no_diff:
+				dn_link = frappe.utils.get_link_to_form("Delivery Note", item.delivery_note)
+				serial_no_msg = ", ".join(frappe.bold(d) for d in serial_no_diff)
+
+				msg = _("Row #{0}: The following Serial Nos are not present in Delivery Note {1}:").format(
+					item.idx, dn_link)
+				msg += " " + serial_no_msg
+
+				frappe.throw(msg=msg, title=_("Serial Nos Mismatch"))
 
 			if item.serial_no and cint(item.qty) != len(si_serial_nos):
-				frappe.throw(_("Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format(
+				frappe.throw(_("Row #{0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format(
 					item.idx, item.qty, item.item_code, len(si_serial_nos)))
 
 	def update_project(self):
@@ -1422,6 +1427,7 @@
 			return
 
 		outstanding_amount = flt(self.outstanding_amount, self.precision("outstanding_amount"))
+		total = get_total_in_party_account_currency(self)
 
 		if not status:
 			if self.docstatus == 2:
@@ -1429,9 +1435,9 @@
 			elif self.docstatus == 1:
 				if self.is_internal_transfer():
 					self.status = 'Internal Transfer'
-				elif is_overdue(self):
+				elif is_overdue(self, total):
 					self.status = "Overdue"
-				elif 0 < outstanding_amount < flt(self.grand_total, self.precision("grand_total")):
+				elif 0 < outstanding_amount < total:
 					self.status = "Partly Paid"
 				elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
 					self.status = "Unpaid"
@@ -1458,27 +1464,42 @@
 		if update:
 			self.db_set('status', self.status, update_modified = update_modified)
 
-def is_overdue(doc):
-	outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
 
+def get_total_in_party_account_currency(doc):
+	total_fieldname = (
+		"grand_total"
+		if doc.disable_rounded_total
+		else "rounded_total"
+	)
+	if doc.party_account_currency != doc.currency:
+		total_fieldname = "base_" + total_fieldname
+
+	return flt(doc.get(total_fieldname), doc.precision(total_fieldname))
+
+def is_overdue(doc, total):
+	outstanding_amount = flt(doc.outstanding_amount, doc.precision("outstanding_amount"))
 	if outstanding_amount <= 0:
 		return
 
-	grand_total = flt(doc.grand_total, doc.precision("grand_total"))
-	nowdate = getdate()
-	if doc.payment_schedule:
-		# calculate payable amount till date
-		payable_amount = sum(
-			payment.payment_amount
-			for payment in doc.payment_schedule
-			if getdate(payment.due_date) < nowdate
-		)
+	today = getdate()
+	if doc.get('is_pos') or not doc.get('payment_schedule'):
+		return getdate(doc.due_date) < today
 
-		if (grand_total - outstanding_amount) < payable_amount:
-			return True
+	# calculate payable amount till date
+	payment_amount_field = (
+		"base_payment_amount"
+		if doc.party_account_currency != doc.currency
+		else "payment_amount"
+	)
 
-	elif getdate(doc.due_date) < nowdate:
-		return True
+	payable_amount = sum(
+		payment.get(payment_amount_field)
+		for payment in doc.payment_schedule
+		if getdate(payment.due_date) < today
+	)
+
+	return (total - outstanding_amount) < payable_amount
+
 
 def get_discounting_status(sales_invoice):
 	status = None
@@ -1954,22 +1975,23 @@
 	def append_payment(payment_mode):
 		payment = doc.append('payments', {})
 		payment.default = payment_mode.default
-		payment.mode_of_payment = payment_mode.parent
+		payment.mode_of_payment = payment_mode.mop
 		payment.account = payment_mode.default_account
 		payment.type = payment_mode.type
 
 	doc.set('payments', [])
 	invalid_modes = []
-	for pos_payment_method in pos_profile.get('payments'):
-		pos_payment_method = pos_payment_method.as_dict()
+	mode_of_payments = [d.mode_of_payment for d in pos_profile.get('payments')]
+	mode_of_payments_info = get_mode_of_payments_info(mode_of_payments, doc.company)
 
-		payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company)
+	for row in pos_profile.get('payments'):
+		payment_mode = mode_of_payments_info.get(row.mode_of_payment)
 		if not payment_mode:
-			invalid_modes.append(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment))
+			invalid_modes.append(get_link_to_form("Mode of Payment", row.mode_of_payment))
 			continue
 
-		payment_mode[0].default = pos_payment_method.default
-		append_payment(payment_mode[0])
+		payment_mode.default = row.default
+		append_payment(payment_mode)
 
 	if invalid_modes:
 		if invalid_modes == 1:
@@ -1985,6 +2007,24 @@
 		where mpa.parent = mp.name and mpa.company = %(company)s and mp.enabled = 1""",
 	{'company': doc.company}, as_dict=1)
 
+def get_mode_of_payments_info(mode_of_payments, company):
+	data = frappe.db.sql(
+		"""
+		select
+			mpa.default_account, mpa.parent as mop, mp.type as type
+		from
+			`tabMode of Payment Account` mpa,`tabMode of Payment` mp
+		where
+			mpa.parent = mp.name and
+			mpa.company = %s and
+			mp.enabled = 1 and
+			mp.name in (%s)
+		group by
+			mp.name
+		""", (company, mode_of_payments), as_dict=1)
+
+	return {row.get('mop'): row for row in data}
+
 def get_mode_of_payment_info(mode_of_payment, company):
 	return frappe.db.sql("""
 		select mpa.default_account, mpa.parent, mp.type as type
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 8a2e945..56de3c6 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1087,8 +1087,6 @@
 
 		actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
 
-		frappe.db.commit()
-
 		self.assertEqual(actual_qty_0 - 5, actual_qty_1)
 
 		# outgoing_rate
@@ -2023,11 +2021,7 @@
 		frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
 
-	def test_sle_if_target_warehouse_exists_accidentally(self):
-		"""
-			Check if inward entry exists if Target Warehouse accidentally exists
-			but Customer is not an internal customer.
-		"""
+	def test_sle_for_target_warehouse(self):
 		se = make_stock_entry(
 			item_code="138-CMS Shoe",
 			target="Finished Goods - _TC",
@@ -2048,9 +2042,9 @@
 		sles = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": si.name},
 			fields=["name", "actual_qty"])
 
-		# check if only one SLE for outward entry is created
-		self.assertEqual(len(sles), 1)
-		self.assertEqual(sles[0].actual_qty, -1)
+		# check if both SLEs are created
+		self.assertEqual(len(sles), 2)
+		self.assertEqual(sum(d.actual_qty for d in sles), 0.0)
 
 		# tear down
 		si.cancel()
@@ -2361,6 +2355,18 @@
 		si.reload()
 		self.assertEqual(si.status, "Paid")
 
+	def test_sales_invoice_submission_post_account_freezing_date(self):
+		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', add_days(getdate(), 1))
+		si = create_sales_invoice(do_not_save=True)
+		si.posting_date = add_days(getdate(), 1)
+		si.save()
+
+		self.assertRaises(frappe.ValidationError, si.submit)
+		si.posting_date = getdate()
+		si.submit()
+
+		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+
 def get_sales_invoice_for_e_invoice():
 	si = make_sales_invoice_for_ewaybill()
 	si.naming_series = 'INV-2020-.#####'
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 8171b3b..de95502 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -33,7 +33,7 @@
 		# update start just before the subscription doc is created
 		self.update_subscription_period(self.start_date)
 
-	def update_subscription_period(self, date=None):
+	def update_subscription_period(self, date=None, return_date=False):
 		"""
 		Subscription period is the period to be billed. This method updates the
 		beginning of the billing period and end of the billing period.
@@ -41,28 +41,41 @@
 		The beginning of the billing period is represented in the doctype as
 		`current_invoice_start` and the end of the billing period is represented
 		as `current_invoice_end`.
-		"""
-		self.set_current_invoice_start(date)
-		self.set_current_invoice_end()
 
-	def set_current_invoice_start(self, date=None):
+		If return_date is True, it wont update the start and end dates.
+		This is implemented to get the dates to check if is_current_invoice_generated
 		"""
-		This sets the date of the beginning of the current billing period.
+		_current_invoice_start = self.get_current_invoice_start(date)
+		_current_invoice_end = self.get_current_invoice_end(_current_invoice_start)
+
+		if return_date:
+			return _current_invoice_start, _current_invoice_end
+
+		self.current_invoice_start = _current_invoice_start
+		self.current_invoice_end = _current_invoice_end
+
+	def get_current_invoice_start(self, date=None):
+		"""
+		This returns the date of the beginning of the current billing period.
 		If the `date` parameter is not given , it will be automatically set as today's
 		date.
 		"""
-		if self.is_new_subscription() and self.trial_period_end and getdate(self.trial_period_end) > getdate(self.start_date):
-			self.current_invoice_start = add_days(self.trial_period_end, 1)
-		elif self.trial_period_start and self.is_trialling():
-			self.current_invoice_start = self.trial_period_start
-		elif date:
-			self.current_invoice_start = date
-		else:
-			self.current_invoice_start = nowdate()
+		_current_invoice_start = None
 
-	def set_current_invoice_end(self):
+		if self.is_new_subscription() and self.trial_period_end and getdate(self.trial_period_end) > getdate(self.start_date):
+			_current_invoice_start = add_days(self.trial_period_end, 1)
+		elif self.trial_period_start and self.is_trialling():
+			_current_invoice_start = self.trial_period_start
+		elif date:
+			_current_invoice_start = date
+		else:
+			_current_invoice_start = nowdate()
+
+		return _current_invoice_start
+
+	def get_current_invoice_end(self, date=None):
 		"""
-		This sets the date of the end of the current billing period.
+		This returns the date of the end of the current billing period.
 
 		If the subscription is in trial period, it will be set as the end of the
 		trial period.
@@ -71,44 +84,47 @@
 		current billing period where `x` is the billing interval from the
 		`Subscription Plan` in the `Subscription`.
 		"""
-		if self.is_trialling() and getdate(self.current_invoice_start) < getdate(self.trial_period_end):
-			self.current_invoice_end = self.trial_period_end
+		_current_invoice_end = None
+
+		if self.is_trialling() and getdate(date) < getdate(self.trial_period_end):
+			_current_invoice_end = self.trial_period_end
 		else:
 			billing_cycle_info = self.get_billing_cycle_data()
 			if billing_cycle_info:
-				if self.is_new_subscription() and getdate(self.start_date) < getdate(self.current_invoice_start):
-					self.current_invoice_end = add_to_date(self.start_date, **billing_cycle_info)
+				if self.is_new_subscription() and getdate(self.start_date) < getdate(date):
+					_current_invoice_end = add_to_date(self.start_date, **billing_cycle_info)
 
 					# For cases where trial period is for an entire billing interval
-					if getdate(self.current_invoice_end) < getdate(self.current_invoice_start):
-						self.current_invoice_end = add_to_date(self.current_invoice_start, **billing_cycle_info)
+					if getdate(self.current_invoice_end) < getdate(date):
+						_current_invoice_end = add_to_date(date, **billing_cycle_info)
 				else:
-					self.current_invoice_end = add_to_date(self.current_invoice_start, **billing_cycle_info)
+					_current_invoice_end = add_to_date(date, **billing_cycle_info)
 			else:
-				self.current_invoice_end = get_last_day(self.current_invoice_start)
+				_current_invoice_end = get_last_day(date)
 
 			if self.follow_calendar_months:
 				billing_info = self.get_billing_cycle_and_interval()
 				billing_interval_count = billing_info[0]['billing_interval_count']
 				calendar_months = get_calendar_months(billing_interval_count)
 				calendar_month = 0
-				current_invoice_end_month = getdate(self.current_invoice_end).month
-				current_invoice_end_year = getdate(self.current_invoice_end).year
+				current_invoice_end_month = getdate(_current_invoice_end).month
+				current_invoice_end_year = getdate(_current_invoice_end).year
 
 				for month in calendar_months:
 					if month <= current_invoice_end_month:
 						calendar_month = month
 
 				if cint(calendar_month - billing_interval_count) <= 0 and \
-					getdate(self.current_invoice_start).month != 1:
+					getdate(date).month != 1:
 					calendar_month = 12
 					current_invoice_end_year -= 1
 
-				self.current_invoice_end = get_last_day(cstr(current_invoice_end_year) + '-' \
-					+ cstr(calendar_month) + '-01')
+				_current_invoice_end = get_last_day(cstr(current_invoice_end_year) + '-' + cstr(calendar_month) + '-01')
 
-			if self.end_date and getdate(self.current_invoice_end) > getdate(self.end_date):
-				self.current_invoice_end = self.end_date
+			if self.end_date and getdate(_current_invoice_end) > getdate(self.end_date):
+				_current_invoice_end = self.end_date
+
+		return _current_invoice_end
 
 	@staticmethod
 	def validate_plans_billing_cycle(billing_cycle_data):
@@ -488,8 +504,9 @@
 
 	def is_current_invoice_generated(self):
 		invoice = self.get_current_invoice()
+		_current_start_date, _current_end_date = self.update_subscription_period(date=add_days(self.current_invoice_end, 1), return_date=True)
 
-		if invoice and getdate(self.current_invoice_start) <= getdate(invoice.posting_date) <= getdate(self.current_invoice_end):
+		if invoice and getdate(_current_start_date) <= getdate(invoice.posting_date) <= getdate(_current_end_date):
 			return True
 
 		return False
@@ -542,15 +559,15 @@
 			else:
 				self.set_status_grace_period()
 
-			if getdate() > getdate(self.current_invoice_end):
-				self.update_subscription_period(add_days(self.current_invoice_end, 1))
-
 			# Generate invoices periodically even if current invoice are unpaid
 			if self.generate_new_invoices_past_due_date and not self.is_current_invoice_generated() and (self.is_postpaid_to_invoice()
 				or self.is_prepaid_to_invoice()):
 				prorate = frappe.db.get_single_value('Subscription Settings', 'prorate')
 				self.generate_invoice(prorate)
 
+			if getdate() > getdate(self.current_invoice_end):
+				self.update_subscription_period(add_days(self.current_invoice_end, 1))
+
 	@staticmethod
 	def is_paid(invoice):
 		"""
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index e2cf4d5..0f7a0a8 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -18,6 +18,7 @@
 
 from erpnext.accounts.doctype.subscription.subscription import get_prorata_factor
 
+test_dependencies = ("UOM", "Item Group", "Item")
 
 def create_plan():
 	if not frappe.db.exists('Subscription Plan', '_Test Plan Name'):
@@ -68,7 +69,6 @@
 		supplier.insert()
 
 class TestSubscription(unittest.TestCase):
-
 	def setUp(self):
 		create_plan()
 
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js
index b8d6c9a..7b47974 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js
@@ -8,7 +8,8 @@
 			if (child.company) {
 				return {
 					filters: {
-						'company': child.company
+						'company': child.company,
+						'root_type': ['in', ['Asset', 'Liability']]
 					}
 				};
 			}
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 16ef5fc..c36f3cb 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -13,6 +13,7 @@
 class TaxWithholdingCategory(Document):
 	def validate(self):
 		self.validate_dates()
+		self.validate_accounts()
 		self.validate_thresholds()
 
 	def validate_dates(self):
@@ -25,6 +26,14 @@
 			if last_date and getdate(d.to_date) < getdate(last_date):
 				frappe.throw(_("Row #{0}: Dates overlapping with other row").format(d.idx))
 
+	def validate_accounts(self):
+		existing_accounts = []
+		for d in self.get('accounts'):
+			if d.get('account') in existing_accounts:
+				frappe.throw(_("Account {0} added multiple times").format(frappe.bold(d.get('account'))))
+
+			existing_accounts.append(d.get('account'))
+
 	def validate_thresholds(self):
 		for d in self.get('rates'):
 			if d.cumulative_threshold and d.cumulative_threshold < d.single_threshold:
@@ -203,6 +212,9 @@
 			# then chargeable value is "prev invoices + advances" value which cross the threshold
 			tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
 
+	if cint(tax_details.round_off_tax_amount):
+		tax_amount = round(tax_amount)
+
 	return tax_amount, tax_deducted
 
 def get_invoice_vouchers(parties, tax_details, company, party_type='Supplier'):
@@ -322,9 +334,6 @@
 		else:
 			tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
 
-	if cint(tax_details.round_off_tax_amount):
-		tds_amount = round(tds_amount)
-
 	return tds_amount
 
 def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 0cee6f5..0cae16b 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -293,7 +293,7 @@
 		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() or frappe.session.user == 'Administrator':
+					and (frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == 'Administrator'):
 				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):
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py
index d527188..bb8138b 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.py
+++ b/erpnext/accounts/report/cash_flow/cash_flow.py
@@ -139,9 +139,9 @@
 	data["total"] = total
 	return data
 
-def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters={}):
+def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None):
 	cond = ""
-	filters = frappe._dict(filters)
+	filters = frappe._dict(filters or {})
 
 	if filters.include_default_book_entries:
 		company_fb = frappe.db.get_value("Company", company, 'default_finance_book')
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
index 6a8301a..e24a5f9 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -103,8 +103,11 @@
 				column.is_tree = true;
 			}
 
-			value = default_formatter(value, row, column, data);
+			if (data && data.account && column.apply_currency_formatter) {
+				data.currency = erpnext.get_currency(column.company_name);
+			}
 
+			value = default_formatter(value, row, column, data);
 			if (!data.parent_account) {
 				value = $(`<span>${value}</span>`);
 
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 b0cfbac..0de2a98 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -3,12 +3,14 @@
 
 from __future__ import unicode_literals
 
+from collections import defaultdict
+
 import frappe
 from frappe import _
 from frappe.utils import cint, flt, getdate
 
+import erpnext
 from erpnext.accounts.report.balance_sheet.balance_sheet import (
-	check_opening_balance,
 	get_chart_data,
 	get_provisional_profit_loss,
 )
@@ -31,7 +33,7 @@
 from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
 	get_report_summary as get_pl_summary,
 )
-from erpnext.accounts.report.utils import convert_to_presentation_currency
+from erpnext.accounts.report.utils import convert, convert_to_presentation_currency
 
 
 def execute(filters=None):
@@ -42,7 +44,7 @@
 
 	fiscal_year = get_fiscal_year_data(filters.get('from_fiscal_year'), filters.get('to_fiscal_year'))
 	companies_column, companies = get_companies(filters)
-	columns = get_columns(companies_column)
+	columns = get_columns(companies_column, filters)
 
 	if filters.get('report') == "Balance Sheet":
 		data, message, chart, report_summary = get_balance_sheet_data(fiscal_year, companies, columns, filters)
@@ -73,21 +75,24 @@
 	provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
 		companies, filters.get('company'), company_currency, True)
 
-	message, opening_balance = check_opening_balance(asset, liability, equity)
+	message, opening_balance = prepare_companywise_opening_balance(asset, liability, equity, companies)
 
-	if opening_balance and round(opening_balance,2) !=0:
-		unclosed ={
+	if opening_balance:
+		unclosed = {
 			"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
 			"warn_if_negative": True,
 			"currency": company_currency
 		}
-		for company in companies:
-			unclosed[company] = opening_balance
-			if provisional_profit_loss:
-				provisional_profit_loss[company] = provisional_profit_loss[company] - opening_balance
 
-		unclosed["total"]=opening_balance
+		for company in companies:
+			unclosed[company] = opening_balance.get(company)
+			if provisional_profit_loss and provisional_profit_loss.get(company):
+				provisional_profit_loss[company] = (
+					flt(provisional_profit_loss[company]) - flt(opening_balance.get(company))
+				)
+
+		unclosed["total"] = opening_balance.get(company)
 		data.append(unclosed)
 
 	if provisional_profit_loss:
@@ -102,6 +107,37 @@
 
 	return data, message, chart, report_summary
 
+def prepare_companywise_opening_balance(asset_data, liability_data, equity_data, companies):
+	opening_balance = {}
+	for company in companies:
+		opening_value = 0
+
+		# opening_value = Aseet - liability - equity
+		for data in [asset_data, liability_data, equity_data]:
+			account_name = get_root_account_name(data[0].root_type, company)
+			opening_value += (get_opening_balance(account_name, data, company) or 0.0)
+
+		opening_balance[company] = opening_value
+
+	if opening_balance:
+		return _("Previous Financial Year is not closed"), opening_balance
+
+	return '', {}
+
+def get_opening_balance(account_name, data, company):
+	for row in data:
+		if row.get('account_name') == account_name:
+			return row.get('company_wise_opening_bal', {}).get(company, 0.0)
+
+def get_root_account_name(root_type, company):
+	return frappe.get_all(
+		'Account',
+		fields=['account_name'],
+		filters = {'root_type': root_type, 'is_group': 1,
+			'company': company, 'parent_account': ('is', 'not set')},
+		as_list=1
+	)[0][0]
+
 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)
@@ -193,30 +229,37 @@
 	data["total"] = total
 	return data
 
-def get_columns(companies):
-	columns = [{
-		"fieldname": "account",
-		"label": _("Account"),
-		"fieldtype": "Link",
-		"options": "Account",
-		"width": 300
-	}]
-
-	columns.append({
-		"fieldname": "currency",
-		"label": _("Currency"),
-		"fieldtype": "Link",
-		"options": "Currency",
-		"hidden": 1
-	})
+def get_columns(companies, filters):
+	columns = [
+		{
+			"fieldname": "account",
+			"label": _("Account"),
+			"fieldtype": "Link",
+			"options": "Account",
+			"width": 300
+		}, {
+			"fieldname": "currency",
+			"label": _("Currency"),
+			"fieldtype": "Link",
+			"options": "Currency",
+			"hidden": 1
+		}
+	]
 
 	for company in companies:
+		apply_currency_formatter = 1 if not filters.presentation_currency else 0
+		currency = filters.presentation_currency
+		if not currency:
+			currency = erpnext.get_company_currency(company)
+
 		columns.append({
 			"fieldname": company,
-			"label": company,
+			"label": f'{company} ({currency})',
 			"fieldtype": "Currency",
 			"options": "currency",
-			"width": 150
+			"width": 150,
+			"apply_currency_formatter": apply_currency_formatter,
+			"company_name": company
 		})
 
 	return columns
@@ -236,6 +279,8 @@
 		start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
 		end_date = filters.period_end_date
 
+	filters.end_date = end_date
+
 	gl_entries_by_account = {}
 	for root in frappe.db.sql("""select lft, rgt from tabAccount
 			where root_type=%s and ifnull(parent_account, '') = ''""", root_type, as_dict=1):
@@ -244,9 +289,10 @@
 			end_date, root.lft, root.rgt, filters,
 			gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False)
 
-	calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters)
+	calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year)
 	accumulate_values_into_parents(accounts, accounts_by_name, companies)
-	out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency)
+
+	out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters)
 
 	if out:
 		add_total_row(out, root_type, balance_must_be, companies, company_currency)
@@ -257,7 +303,10 @@
 	return (filters.get('presentation_currency')
 		or frappe.get_cached_value('Company',  filters.company,  "default_currency"))
 
-def calculate_values(accounts_by_name, gl_entries_by_account, companies, start_date, filters):
+def calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year):
+	start_date = (fiscal_year.year_start_date
+		if filters.filter_based_on == 'Fiscal Year' else filters.period_start_date)
+
 	for entries in gl_entries_by_account.values():
 		for entry in entries:
 			if entry.account_number:
@@ -266,15 +315,32 @@
 				account_name =  entry.account_name
 
 			d = accounts_by_name.get(account_name)
+
 			if d:
+				debit, credit = 0, 0
 				for company in companies:
 					# check if posting date is within the period
 					if (entry.company == company or (filters.get('accumulated_in_group_company'))
 						and entry.company in companies.get(company)):
-						d[company] = d.get(company, 0.0) + flt(entry.debit) - flt(entry.credit)
+						parent_company_currency = erpnext.get_company_currency(d.company)
+						child_company_currency = erpnext.get_company_currency(entry.company)
+
+						debit, credit = flt(entry.debit), flt(entry.credit)
+
+						if (not filters.get('presentation_currency')
+							and entry.company != company
+							and parent_company_currency != child_company_currency
+							and filters.get('accumulated_in_group_company')):
+							debit = convert(debit, parent_company_currency, child_company_currency, filters.end_date)
+							credit = convert(credit, parent_company_currency, child_company_currency, filters.end_date)
+
+						d[company] = d.get(company, 0.0) + flt(debit) - flt(credit)
+
+						if entry.posting_date < getdate(start_date):
+							d['company_wise_opening_bal'][company] += (flt(debit) - flt(credit))
 
 				if entry.posting_date < getdate(start_date):
-					d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
+					d["opening_balance"] = d.get("opening_balance", 0.0) + flt(debit) - flt(credit)
 
 def accumulate_values_into_parents(accounts, accounts_by_name, companies):
 	"""accumulate children's values in parent accounts"""
@@ -282,17 +348,18 @@
 		if d.parent_account:
 			account = d.parent_account_name
 
-			if not accounts_by_name.get(account):
-				continue
+			# if not accounts_by_name.get(account):
+			# 	continue
 
 			for company in companies:
 				accounts_by_name[account][company] = \
 					accounts_by_name[account].get(company, 0.0) + d.get(company, 0.0)
 
+				accounts_by_name[account]['company_wise_opening_bal'][company] += d.get('company_wise_opening_bal', {}).get(company, 0.0)
+
 			accounts_by_name[account]["opening_balance"] = \
 				accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
 
-
 def get_account_heads(root_type, companies, filters):
 	accounts = get_accounts(root_type, filters)
 
@@ -353,7 +420,7 @@
 			`tabAccount` where company = %s and root_type = %s
 		""" , (filters.get('company'), root_type), as_dict=1)
 
-def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency):
+def prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters):
 	data = []
 
 	for d in accounts:
@@ -367,10 +434,13 @@
 			"parent_account": _(d.parent_account),
 			"indent": flt(d.indent),
 			"year_start_date": start_date,
+			"root_type": d.root_type,
 			"year_end_date": end_date,
-			"currency": company_currency,
+			"currency": filters.presentation_currency,
+			"company_wise_opening_bal": d.company_wise_opening_bal,
 			"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be == "Debit" else -1)
 		})
+
 		for company in companies:
 			if d.get(company) and balance_must_be == "Credit":
 				# change sign based on Debit or Credit, since calculation is done using (debit - credit)
@@ -385,6 +455,7 @@
 
 		row["has_value"] = has_value
 		row["total"] = total
+
 		data.append(row)
 
 	return data
@@ -447,6 +518,7 @@
 		'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1)
 
 def validate_entries(key, entry, accounts_by_name, accounts):
+	# If an account present in the child company and not in the parent company
 	if key not in accounts_by_name:
 		args = get_account_details(entry.account)
 
@@ -456,12 +528,23 @@
 			args.update({
 				'lft': parent_args.lft + 1,
 				'rgt': parent_args.rgt - 1,
+				'indent': 3,
 				'root_type': parent_args.root_type,
-				'report_type': parent_args.report_type
+				'report_type': parent_args.report_type,
+				'parent_account_name': parent_args.account_name,
+				'company_wise_opening_bal': defaultdict(float)
 			})
 
 		accounts_by_name.setdefault(key, args)
-		accounts.append(args)
+
+		idx = len(accounts)
+		# To identify parent account index
+		for index, row in enumerate(accounts):
+			if row.parent_account_name == args.parent_account_name:
+				idx = index
+				break
+
+		accounts.insert(idx+1, args)
 
 def get_additional_conditions(from_date, ignore_closing_entries, filters):
 	additional_conditions = []
@@ -491,7 +574,6 @@
 			for company in companies:
 				total_row.setdefault(company, 0.0)
 				total_row[company] += row.get(company, 0.0)
-				row[company] = 0.0
 
 			total_row.setdefault("total", 0.0)
 			total_row["total"] += flt(row["total"])
@@ -511,6 +593,7 @@
 			account_name = d.account_number + ' - ' + d.account_name
 		else:
 			account_name =  d.account_name
+		d['company_wise_opening_bal'] = defaultdict(float)
 		accounts_by_name[account_name] = d
 
 		parent_children_map.setdefault(d.parent_account or None, []).append(d)
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 5bd6e58..31416da 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -155,6 +155,8 @@
 
 	if filters.get("group_by") == "Group by Voucher":
 		order_by_statement = "order by posting_date, voucher_type, voucher_no"
+	if filters.get("group_by") == "Group by Account":
+		order_by_statement = "order by account, posting_date, creation"
 
 	if filters.get("include_default_book_entries"):
 		filters['company_fb'] = frappe.db.get_value("Company",
@@ -421,8 +423,6 @@
 			update_value_in_dict(totals, 'closing', gle)
 
 		elif gle.posting_date <= to_date:
-			update_value_in_dict(gle_map[gle.get(group_by)].totals, 'total', gle)
-			update_value_in_dict(totals, 'total', gle)
 			if filters.get("group_by") != 'Group by Voucher (Consolidated)':
 				gle_map[gle.get(group_by)].entries.append(gle)
 			elif filters.get("group_by") == 'Group by Voucher (Consolidated)':
@@ -436,10 +436,11 @@
 				else:
 					update_value_in_dict(consolidated_gle, key, gle)
 
-			update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle)
-			update_value_in_dict(totals, 'closing', gle)
-
 	for key, value in consolidated_gle.items():
+		update_value_in_dict(gle_map[value.get(group_by)].totals, 'total', value)
+		update_value_in_dict(totals, 'total', value)
+		update_value_in_dict(gle_map[value.get(group_by)].totals, 'closing', value)
+		update_value_in_dict(totals, 'closing', value)
 		entries.append(value)
 
 	return totals, entries
diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
index 621b697..6a7f2e5 100644
--- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
+++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py
@@ -44,16 +44,16 @@
 
 		if rate and tds_deducted:
 			row = {
-				'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier).pan,
-				'supplier': supplier_map.get(supplier).name
+				'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
+				'supplier': supplier_map.get(supplier, {}).get('name')
 			}
 
 			if filters.naming_series == 'Naming Series':
-				row.update({'supplier_name': supplier_map.get(supplier).supplier_name})
+				row.update({'supplier_name': supplier_map.get(supplier, {}).get('supplier_name')})
 
 			row.update({
 				'section_code': tax_withholding_category,
-				'entity_type': supplier_map.get(supplier).supplier_type,
+				'entity_type': supplier_map.get(supplier, {}).get('supplier_type'),
 				'tds_rate': rate,
 				'total_amount_credited': total_amount_credited,
 				'tds_deducted': tds_deducted,
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index 2b26ac5..33d1748 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Profit and Loss",
@@ -8,18 +7,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Accounts\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Profit and Loss\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chart of Accounts\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Journal Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payment Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Trial Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounting Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Payable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Financial Statements\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Multi Currency\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bank Statement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Subscription Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goods and Services Tax (GST India)\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Share Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Cost Center and Budgeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening and Closing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Profitability\", \"col\": 4}}]",
  "creation": "2020-03-02 15:41:59.515192",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "accounting",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Accounting",
  "links": [
   {
@@ -534,6 +527,17 @@
    "type": "Link"
   },
   {
+    "dependencies": "GL Entry",
+    "hidden": 0,
+    "is_query_report": 1,
+    "label": "KSA VAT Report",
+    "link_to": "KSA VAT",
+    "link_type": "Report",
+    "onboard": 0,
+    "only_for": "Saudi Arabia",
+    "type": "Link"
+   },
+  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Financial Statements",
@@ -1154,6 +1158,16 @@
    "type": "Link"
   },
   {
+    "hidden": 0,
+    "is_query_report": 0,
+    "label": "KSA VAT Setting",
+    "link_to": "KSA VAT Setting",
+    "link_type": "DocType",
+    "onboard": 0,
+    "only_for": "Saudi Arabia",
+    "type": "Link"
+   },
+  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Profitability",
@@ -1206,15 +1220,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-27 12:15:52.872470",
+ "modified": "2021-08-27 12:15:52.872471",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Accounting",
- "onboarding": "Accounts",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/agriculture/workspace/agriculture/agriculture.json b/erpnext/agriculture/workspace/agriculture/agriculture.json
index 633777e..6714de6 100644
--- a/erpnext/agriculture/workspace/agriculture/agriculture.json
+++ b/erpnext/agriculture/workspace/agriculture/agriculture.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Crops & Lands\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Analytics\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Diseases & Fertilizers\", \"col\": 4}}]",
  "creation": "2020-03-02 17:23:34.339274",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "agriculture",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Agriculture",
  "links": [
   {
@@ -163,15 +156,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:54.595197",
+ "modified": "2021-08-05 12:15:54.595198",
  "modified_by": "Administrator",
  "module": "Agriculture",
  "name": "Agriculture",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "Agriculture",
  "roles": [],
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 7e135be..99a6cc3 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -194,7 +194,7 @@
 			start = self.clear_depreciation_schedule()
 
 			# value_after_depreciation - current Asset value
-			if d.value_after_depreciation:
+			if self.docstatus == 1 and d.value_after_depreciation:
 				value_after_depreciation = (flt(d.value_after_depreciation) -
 					flt(self.opening_accumulated_depreciation))
 			else:
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 7183ee7..cf4581b 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -682,6 +682,27 @@
 		# reset indian company
 		frappe.flags.company = company_flag
 
+	def test_expected_value_change(self):
+		"""
+			tests if changing `expected_value_after_useful_life`
+			affects `value_after_depreciation`
+		"""
+
+		asset = create_asset(calculate_depreciation=1)
+		asset.opening_accumulated_depreciation = 2000
+		asset.number_of_depreciations_booked = 1
+
+		asset.finance_books[0].expected_value_after_useful_life = 100
+		asset.save()
+		asset.reload()
+		self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
+
+		# changing expected_value_after_useful_life shouldn't affect value_after_depreciation
+		asset.finance_books[0].expected_value_after_useful_life = 200
+		asset.save()
+		asset.reload()
+		self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
+
 def create_asset_data():
 	if not frappe.db.exists("Asset Category", "Computers"):
 		create_asset_category()
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
index 9945a32..30e3a52 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -22,7 +22,7 @@
 		frappe.db.sql("delete from `tabTax Rule`")
 
 	def test_update_status(self):
-		asset = create_asset()
+		asset = create_asset(submit=1)
 		initial_status = asset.status
 		asset_repair = create_asset_repair(asset = asset)
 
@@ -76,7 +76,7 @@
 		self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity)
 
 	def test_increase_in_asset_value_due_to_stock_consumption(self):
-		asset = create_asset(calculate_depreciation = 1)
+		asset = create_asset(calculate_depreciation = 1, submit=1)
 		initial_asset_value = get_asset_value(asset)
 		asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1)
 		asset.reload()
@@ -85,7 +85,7 @@
 		self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value)
 
 	def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self):
-		asset = create_asset(calculate_depreciation = 1)
+		asset = create_asset(calculate_depreciation = 1, submit=1)
 		initial_asset_value = get_asset_value(asset)
 		asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
 		asset.reload()
@@ -103,7 +103,7 @@
 		self.assertEqual(asset_repair.name, gl_entry.voucher_no)
 
 	def test_increase_in_asset_life(self):
-		asset = create_asset(calculate_depreciation = 1)
+		asset = create_asset(calculate_depreciation = 1, submit=1)
 		initial_num_of_depreciations = num_of_depreciations(asset)
 		create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
 		asset.reload()
@@ -126,7 +126,7 @@
 	if args.asset:
 		asset = args.asset
 	else:
-		asset = create_asset(is_existing_asset = 1)
+		asset = create_asset(is_existing_asset = 1, submit=1)
 	asset_repair = frappe.new_doc("Asset Repair")
 	asset_repair.update({
 		"asset": asset.name,
diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json
index dfbf1a3..495de46 100644
--- a/erpnext/assets/workspace/assets/assets.json
+++ b/erpnext/assets/workspace/assets/assets.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Asset Value Analytics",
@@ -8,18 +7,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Assets\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Asset Value Analytics\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset Category\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fixed Asset Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assets\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
  "creation": "2020-03-02 15:43:27.634865",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "assets",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Assets",
  "links": [
   {
@@ -179,15 +172,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:54.839452",
+ "modified": "2021-08-05 12:15:54.839453",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Assets",
- "onboarding": "Assets",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js
index 944bb61..32431fc 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.js
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.js
@@ -11,7 +11,7 @@
 	{
 		fieldname: "supp_master_name",
 		title: "Supplier Naming By",
-		description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a  ") + "<a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a>" + __(" choose the 'Naming Series' option."),
+		description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a <a href='https://docs.erpnext.com/docs/user/manual/en/setting-up/settings/naming-series' target='_blank'>Naming Series</a> choose the 'Naming Series' option."),
 	},
 	{
 		fieldname: "buying_price_list",
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 12a09cd..a57d9a9 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -25,7 +25,6 @@
   "column_break0",
   "supplier_group",
   "supplier_type",
-  "pan",
   "allow_purchase_invoice_creation_without_purchase_order",
   "allow_purchase_invoice_creation_without_purchase_receipt",
   "disabled",
@@ -177,11 +176,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "pan",
-   "fieldtype": "Data",
-   "label": "PAN"
-  },
-  {
    "fieldname": "language",
    "fieldtype": "Link",
    "label": "Print Language",
@@ -438,11 +432,12 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2021-09-06 17:37:56.522233",
+ "modified": "2021-10-20 22:03:33.147249",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
  "name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/buying/form_tour/buying_settings/buying_settings.json b/erpnext/buying/form_tour/buying_settings/buying_settings.json
new file mode 100644
index 0000000..fa8c80d
--- /dev/null
+++ b/erpnext/buying/form_tour/buying_settings/buying_settings.json
@@ -0,0 +1,77 @@
+{
+ "creation": "2021-07-28 11:51:42.319984",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:06:56.414584",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Buying Settings",
+ "owner": "Administrator",
+ "reference_doctype": "Buying Settings",
+ "save_on_complete": 0,
+ "steps": [
+  {
+   "description": "When a Supplier is saved, system generates a unique identity or name for that Supplier which can be used to refer the Supplier in various Buying transactions.",
+   "field": "",
+   "fieldname": "supp_master_name",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Supplier Naming By",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Supplier Naming By"
+  },
+  {
+   "description": "Configure what should be the default value of Supplier Group when creating a new Supplier.",
+   "field": "",
+   "fieldname": "supplier_group",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Default Supplier Group",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Default Supplier Group"
+  },
+  {
+   "description": "Item prices will be fetched from this Price List.",
+   "field": "",
+   "fieldname": "buying_price_list",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Default Buying Price List",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Default Buying Price List"
+  },
+  {
+   "description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice or a Purchase Receipt directly without creating a Purchase Order first.",
+   "field": "",
+   "fieldname": "po_required",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Purchase Order Required for Purchase Invoice & Receipt Creation?",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purchase Order Required"
+  },
+  {
+   "description": "If this option is configured \"Yes\", ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first.",
+   "field": "",
+   "fieldname": "pr_required",
+   "fieldtype": "Select",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Is Purchase Receipt Required for Purchase Invoice Creation?",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purchase Receipt Required"
+  }
+ ],
+ "title": "Buying Settings"
+}
\ No newline at end of file
diff --git a/erpnext/buying/form_tour/purchase_order/purchase_order.json b/erpnext/buying/form_tour/purchase_order/purchase_order.json
new file mode 100644
index 0000000..3cc88fb
--- /dev/null
+++ b/erpnext/buying/form_tour/purchase_order/purchase_order.json
@@ -0,0 +1,82 @@
+{
+ "creation": "2021-07-29 14:11:58.271113",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:11:31.436135",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order",
+ "owner": "Administrator",
+ "reference_doctype": "Purchase Order",
+ "save_on_complete": 1,
+ "steps": [
+  {
+   "description": "Select a Supplier",
+   "field": "",
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Supplier",
+   "parent_field": "",
+   "position": "Right",
+   "title": "Supplier"
+  },
+  {
+   "description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
+   "field": "",
+   "fieldname": "schedule_date",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Required By",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Required By"
+  },
+  {
+   "description": "Items to be purchased can be added here.",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Items Table"
+  },
+  {
+   "child_doctype": "Purchase Order Item",
+   "description": "Enter the Item Code.",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 1,
+   "label": "Item Code",
+   "next_step_condition": "eval: doc.item_code",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Item Code"
+  },
+  {
+   "child_doctype": "Purchase Order Item",
+   "description": "Enter the required quantity for the material.",
+   "field": "",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Quantity",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Bottom",
+   "title": "Quantity"
+  }
+ ],
+ "title": "Purchase Order"
+}
\ No newline at end of file
diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json
index 887f85b..84e97a2 100644
--- a/erpnext/buying/module_onboarding/buying/buying.json
+++ b/erpnext/buying/module_onboarding/buying/buying.json
@@ -19,7 +19,7 @@
  "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying",
  "idx": 0,
  "is_complete": 0,
- "modified": "2020-07-08 14:05:28.273641",
+ "modified": "2021-08-24 18:13:42.463776",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying",
@@ -29,22 +29,10 @@
    "step": "Introduction to Buying"
   },
   {
-   "step": "Create a Supplier"
-  },
-  {
-   "step": "Setup your Warehouse"
-  },
-  {
-   "step": "Create a Product"
-  },
-  {
    "step": "Create a Material Request"
   },
   {
    "step": "Create your first Purchase Order"
-  },
-  {
-   "step": "Buying Settings"
   }
  ],
  "subtitle": "Products, Purchases, Analysis, and more.",
diff --git a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
index 9dc493d..28e86ab 100644
--- a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
+++ b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json
@@ -1,19 +1,21 @@
 {
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Material Request",
  "creation": "2020-05-15 14:39:09.818764",
+ "description": "# Track Material Request\n\n\nAlso known as Purchase Request or an Indent, is a document identifying a requirement of a set of items (products or services) for various purposes like procurement, transfer, issue, or manufacturing. Once the Material Request is validated, a purchase manager can take the next actions for purchasing items like requesting RFQ from a supplier or directly placing an order with an identified Supplier.\n\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 1,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-15 14:39:09.818764",
+ "modified": "2021-08-24 18:08:08.347501",
  "modified_by": "Administrator",
  "name": "Create a Material Request",
  "owner": "Administrator",
  "reference_document": "Material Request",
+ "show_form_tour": 1,
  "show_full_form": 1,
- "title": "Create a Material Request",
+ "title": "Track Material Request",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
index 9dbed23..18a3931 100644
--- a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
+++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json
@@ -1,19 +1,21 @@
 {
- "action": "Create Entry",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s create your first Purchase Order",
  "creation": "2020-05-12 18:17:49.976035",
+ "description": "# Create first Purchase Order\n\nPurchase Order is at the heart of your buying transactions. In ERPNext, Purchase Order can can be created against a Purchase Material Request (indent) and Supplier Quotation as well.  Purchase Orders is also linked to Purchase Receipt and Purchase Invoices, allowing you to keep a birds-eye view on your purchase deals.\n\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
  "is_single": 0,
  "is_skipped": 0,
- "modified": "2020-05-12 18:31:56.856112",
+ "modified": "2021-08-24 18:08:08.936484",
  "modified_by": "Administrator",
  "name": "Create your first Purchase Order",
  "owner": "Administrator",
  "reference_document": "Purchase Order",
+ "show_form_tour": 0,
  "show_full_form": 0,
- "title": "Create your first Purchase Order",
+ "title": "Create first Purchase Order",
  "validate_action": 1
 }
\ No newline at end of file
diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
index fd98fdd..01ac8b8 100644
--- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
+++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json
@@ -1,19 +1,22 @@
 {
- "action": "Watch Video",
+ "action": "Show Form Tour",
+ "action_label": "Let\u2019s walk-through few Buying Settings",
  "creation": "2020-05-06 15:37:09.477765",
+ "description": "# Buying Settings\n\n\nBuying module\u2019s features are highly configurable as per your business needs. Buying Settings is the place where you can set your preferences for:\n\n- Supplier naming and default values\n- Billing and shipping preference in buying transactions\n\n\n",
  "docstatus": 0,
  "doctype": "Onboarding Step",
  "idx": 0,
  "is_complete": 0,
- "is_mandatory": 0,
- "is_single": 0,
+ "is_single": 1,
  "is_skipped": 0,
- "modified": "2020-05-12 18:25:08.509900",
+ "modified": "2021-08-24 18:08:08.345735",
  "modified_by": "Administrator",
  "name": "Introduction to Buying",
  "owner": "Administrator",
- "show_full_form": 0,
- "title": "Introduction to Buying",
+ "reference_document": "Buying Settings",
+ "show_form_tour": 1,
+ "show_full_form": 1,
+ "title": "Buying Settings",
  "validate_action": 1,
  "video_url": "https://youtu.be/efFajTTQBa8"
 }
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
index a5b0947..fd23795 100644
--- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -45,7 +45,6 @@
 		pr = make_purchase_receipt(po.name)
 		pr.get("items")[0].cost_center = "Main - _TPC"
 		pr.submit()
-		frappe.db.commit()
 		date_obj = datetime.date(datetime.now())
 
 		po.load_from_db()
diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json
index 6c91e81..380ef36 100644
--- a/erpnext/buying/workspace/buying/buying.json
+++ b/erpnext/buying/workspace/buying/buying.json
@@ -1,27 +1,18 @@
 {
- "cards_label": "",
- "category": "",
  "charts": [
   {
    "chart_name": "Purchase Order Trends",
    "label": "Purchase Order Trends"
   }
  ],
- "charts_label": "",
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Buying\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Purchase Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Buying\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items & Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier Scorecard\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Regional\", \"col\": 4}}]",
  "creation": "2020-01-28 11:50:26.195467",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "buying",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Buying",
  "links": [
   {
@@ -518,15 +509,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:56.218427",
+ "modified": "2021-08-05 12:15:56.218428",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Buying",
- "onboarding": "Buying",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
@@ -572,6 +560,5 @@
    "type": "Dashboard"
   }
  ],
- "shortcuts_label": "",
  "title": "Buying"
 }
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e9b531e..2486012 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1032,7 +1032,7 @@
 
 		if role_allowed_to_over_bill in user_roles and total_overbilled_amt > 0.1:
 			frappe.msgprint(_("Overbilling of {} ignored because you have {} role.")
-					.format(total_overbilled_amt, role_allowed_to_over_bill), title=_("Warning"), indicator="orange")
+					.format(total_overbilled_amt, role_allowed_to_over_bill), indicator="orange", alert=True)
 
 	def throw_overbill_exception(self, item, max_allowed_amt):
 		frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
@@ -1354,8 +1354,8 @@
 			total = 0
 			base_total = 0
 			for d in self.get("payment_schedule"):
-				total += flt(d.payment_amount)
-				base_total += flt(d.base_payment_amount)
+				total += flt(d.payment_amount, d.precision("payment_amount"))
+				base_total += flt(d.base_payment_amount, d.precision("base_payment_amount"))
 
 			base_grand_total = self.get("base_rounded_total") or self.base_grand_total
 			grand_total = self.get("rounded_total") or self.grand_total
@@ -1371,8 +1371,9 @@
 				else:
 					grand_total -= self.get("total_advance")
 					base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
-			if total != flt(grand_total, self.precision("grand_total")) or \
-				base_total != flt(base_grand_total, self.precision("base_grand_total")):
+
+			if flt(total, self.precision("grand_total")) != flt(grand_total, self.precision("grand_total")) or \
+				flt(base_total, self.precision("base_grand_total")) != flt(base_grand_total, self.precision("base_grand_total")):
 				frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
 
 	def is_rounded_total_disabled(self):
@@ -1686,17 +1687,58 @@
 
 def update_invoice_status():
 	"""Updates status as Overdue for applicable invoices. Runs daily."""
+	today = getdate()
 
 	for doctype in ("Sales Invoice", "Purchase Invoice"):
 		frappe.db.sql("""
-			update `tab{}` as dt set dt.status = 'Overdue'
-			where dt.docstatus = 1
-				and dt.status != 'Overdue'
-				and dt.outstanding_amount > 0
-				and (dt.grand_total - dt.outstanding_amount) <
-					(select sum(payment_amount) from `tabPayment Schedule` as ps
-						where ps.parent = dt.name and ps.due_date < %s)
-		""".format(doctype), getdate())
+			UPDATE `tab{doctype}` invoice SET invoice.status = 'Overdue'
+			WHERE invoice.docstatus = 1
+				AND invoice.status REGEXP '^Unpaid|^Partly Paid'
+				AND invoice.outstanding_amount > 0
+				AND (
+						{or_condition}
+						(
+							(
+								CASE
+									WHEN invoice.party_account_currency = invoice.currency
+									THEN (
+										CASE
+											WHEN invoice.disable_rounded_total
+											THEN invoice.grand_total
+											ELSE invoice.rounded_total
+										END
+									)
+									ELSE (
+										CASE
+											WHEN invoice.disable_rounded_total
+											THEN invoice.base_grand_total
+											ELSE invoice.base_rounded_total
+										END
+									)
+								END
+							) - invoice.outstanding_amount
+						) < (
+							SELECT SUM(
+								CASE
+									WHEN invoice.party_account_currency = invoice.currency
+									THEN ps.payment_amount
+									ELSE ps.base_payment_amount
+								END
+							)
+							FROM `tabPayment Schedule` ps
+							WHERE ps.parent = invoice.name
+								AND ps.due_date < %(today)s
+						)
+					)
+		""".format(
+				doctype=doctype,
+				or_condition=(
+					"invoice.is_pos AND invoice.due_date < %(today)s OR"
+					if doctype == "Sales Invoice"
+					else ""
+				)
+			), {"today": today}
+		)
 
 @frappe.whitelist()
 def get_payment_terms(terms_template, posting_date=None, grand_total=None, base_grand_total=None, bill_date=None):
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 0158a11..bb269f3 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -424,7 +424,7 @@
 					or (cint(self.is_return) and self.docstatus==2)):
 						sl_entries.append(self.get_sle_for_source_warehouse(d))
 
-				if d.target_warehouse and self.get("is_internal_customer"):
+				if d.target_warehouse:
 					sl_entries.append(self.get_sle_for_target_warehouse(d))
 
 				if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
@@ -559,6 +559,12 @@
 				frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
 					.format(d.idx, warehouse, warehouse))
 
+		if not self.get("is_internal_customer") and any(d.get("target_warehouse") for d in items):
+			msg = _("Target Warehouse is set for some items but the customer is not an internal customer.")
+			msg += " " + _("This {} will be treated as material transfer.").format(_(self.doctype))
+			frappe.msgprint(msg, title="Internal Transfer", alert=True)
+
+
 	def validate_items(self):
 		# validate items to see if they have is_sales_item enabled
 		from erpnext.controllers.buying_controller import validate_item_type
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 8738204..49a76da 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -216,11 +216,14 @@
 		overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
 			item[args['target_ref_field']]) * 100
 
-		if overflow_percent - allowance > 0.01 and role not in frappe.get_roles():
+		if overflow_percent - allowance > 0.01:
 			item['max_allowed'] = flt(item[args['target_ref_field']] * (100+allowance)/100)
 			item['reduce_by'] = item[args['target_field']] - item['max_allowed']
 
-			self.limits_crossed_error(args, item, qty_or_amount)
+			if role not in frappe.get_roles():
+				self.limits_crossed_error(args, item, qty_or_amount)
+			else:
+				self.warn_about_bypassing_with_role(item, qty_or_amount, role)
 
 	def limits_crossed_error(self, args, item, qty_or_amount):
 		'''Raise exception for limits crossed'''
@@ -238,6 +241,19 @@
 				frappe.bold(item.get('item_code'))
 			) + '<br><br>' + action_msg, OverAllowanceError, title = _('Limit Crossed'))
 
+	def warn_about_bypassing_with_role(self, item, qty_or_amount, role):
+		action = _("Over Receipt/Delivery") if qty_or_amount == "qty" else _("Overbilling")
+
+		msg = (_("{} of {} {} ignored for item {} because you have {} role.")
+				.format(
+					action,
+					_(item["target_ref_field"].title()),
+					frappe.bold(item["reduce_by"]),
+					frappe.bold(item.get('item_code')),
+					role)
+				)
+		frappe.msgprint(msg, indicator="orange", alert=True)
+
 	def update_qty(self, update_modified=True):
 		"""Updates qty or amount at row level
 
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 78a6e52..08d422d 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -79,8 +79,15 @@
 	def clean_serial_nos(self):
 		for row in self.get("items"):
 			if hasattr(row, "serial_no") and row.serial_no:
-				# replace commas by linefeed and remove all spaces in string
-				row.serial_no = row.serial_no.replace(",", "\n").replace(" ", "")
+				# replace commas by linefeed
+				row.serial_no = row.serial_no.replace(",", "\n")
+
+				# strip preceeding and succeeding spaces for each SN
+				# (SN could have valid spaces in between e.g. SN - 123 - 2021)
+				serial_no_list = row.serial_no.split("\n")
+				serial_no_list = [sn.strip() for sn in serial_no_list]
+
+				row.serial_no = "\n".join(serial_no_list)
 
 	def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
 			default_cost_center=None):
@@ -591,7 +598,7 @@
 
 	data = frappe.db.sql("""
 		select item_code, warehouse, count(name) as total_row
-		from `tabStock Ledger Entry`
+		from `tabStock Ledger Entry` force index (item_warehouse)
 		where
 			({})
 			and timestamp(posting_date, posting_time)
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 95cf032..999599c 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -51,7 +51,7 @@
 		}
 	}
 
-	add_lead_to_prospect (frm) {
+	add_lead_to_prospect () {
 		frappe.prompt([
 			{
 				fieldname: 'prospect',
@@ -65,7 +65,7 @@
 			frappe.call({
 				method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect',
 				args: {
-					'lead': frm.doc.name,
+					'lead': cur_frm.doc.name,
 					'prospect': data.prospect
 				},
 				callback: function(r) {
@@ -79,41 +79,41 @@
 		}, __('Add Lead to Prospect'), __('Add'));
 	}
 
-	make_customer (frm) {
+	make_customer () {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.lead.lead.make_customer",
-			frm: frm
+			frm: cur_frm
 		})
 	}
 
-	make_opportunity (frm) {
+	make_opportunity () {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.lead.lead.make_opportunity",
-			frm: frm
+			frm: cur_frm
 		})
 	}
 
-	make_quotation (frm) {
+	make_quotation () {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.crm.doctype.lead.lead.make_quotation",
-			frm: frm
+			frm: cur_frm
 		})
 	}
 
-	make_prospect (frm) {
+	make_prospect () {
 		frappe.model.with_doctype("Prospect", function() {
 			let prospect = frappe.model.get_new_doc("Prospect");
-			prospect.company_name = frm.doc.company_name;
-			prospect.no_of_employees = frm.doc.no_of_employees;
-			prospect.industry = frm.doc.industry;
-			prospect.market_segment = frm.doc.market_segment;
-			prospect.territory = frm.doc.territory;
-			prospect.fax = frm.doc.fax;
-			prospect.website = frm.doc.website;
-			prospect.prospect_owner = frm.doc.lead_owner;
+			prospect.company_name = cur_frm.doc.company_name;
+			prospect.no_of_employees = cur_frm.doc.no_of_employees;
+			prospect.industry = cur_frm.doc.industry;
+			prospect.market_segment = cur_frm.doc.market_segment;
+			prospect.territory = cur_frm.doc.territory;
+			prospect.fax = cur_frm.doc.fax;
+			prospect.website = cur_frm.doc.website;
+			prospect.prospect_owner = cur_frm.doc.lead_owner;
 
 			let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead');
-			lead_prospect_row.lead = frm.doc.name;
+			lead_prospect_row.lead = cur_frm.doc.name;
 
 			frappe.set_route("Form", "Prospect", prospect.name);
 		});
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index be843a3..55e0efa 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -33,6 +33,7 @@
 		self.validate_item_details()
 		self.validate_uom_is_integer("uom", "qty")
 		self.validate_cust_name()
+		self.map_fields()
 
 		if not self.title:
 			self.title = self.customer_name
@@ -43,6 +44,15 @@
 		else:
 			self.calculate_totals()
 
+	def map_fields(self):
+		for field in self.meta.fields:
+			if not self.get(field.fieldname):
+				try:
+					value = frappe.db.get_value(self.opportunity_from, self.party_name, field.fieldname)
+					frappe.db.set(self, field.fieldname, value)
+				except Exception:
+					continue
+
 	def calculate_totals(self):
 		total = base_total = 0
 		for item in self.get('items'):
diff --git a/erpnext/crm/workspace/crm/crm.json b/erpnext/crm/workspace/crm/crm.json
index a661b62..5a63dc1 100644
--- a/erpnext/crm/workspace/crm/crm.json
+++ b/erpnext/crm/workspace/crm/crm.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Territory Wise Sales"
@@ -7,18 +6,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"CRM\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Lead\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Opportunity\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Customer\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Sales Pipeline\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Campaign\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
  "creation": "2020-01-23 14:48:30.183272",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "crm",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "CRM",
  "links": [
   {
@@ -421,15 +414,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-19 19:08:08.728876",
+ "modified": "2021-08-20 12:15:56.913092",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "CRM",
- "onboarding": "CRM",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index ae498ba..be4ee56 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -138,7 +138,9 @@
 			enrollment.submit()
 			return enrollment
 
-	def enroll_in_course(self, course_name, program_enrollment, enrollment_date=frappe.utils.datetime.datetime.now()):
+	def enroll_in_course(self, course_name, program_enrollment, enrollment_date=None):
+		if enrollment_date is None:
+			enrollment_date = frappe.utils.datetime.datetime.now()
 		try:
 			enrollment = frappe.get_doc({
 					"doctype": "Course Enrollment",
diff --git a/erpnext/education/workspace/education/education.json b/erpnext/education/workspace/education/education.json
index c58ddd6..1465295 100644
--- a/erpnext/education/workspace/education/education.json
+++ b/erpnext/education/workspace/education/education.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Program Enrollments",
@@ -8,18 +7,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Education\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Program Enrollments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Instructor\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Program\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fees\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course Scheduling Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Attendance Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Student and Instructor\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Content Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Admission\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fees\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Schedule\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"LMS Activity\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
  "creation": "2020-03-02 17:22:57.066401",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "education",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Education",
  "links": [
   {
@@ -699,15 +692,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:57.929275",
+ "modified": "2021-08-05 12:15:57.929276",
  "modified_by": "Administrator",
  "module": "Education",
  "name": "Education",
- "onboarding": "Education",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "Education",
  "roles": [],
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/erpnext_integrations/doctype/taxjar_nexus/__init__.py
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json
new file mode 100644
index 0000000..d4d4a51
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.json
@@ -0,0 +1,51 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2021-09-11 05:09:53.773838",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "region",
+  "region_code",
+  "country",
+  "country_code"
+ ],
+ "fields": [
+  {
+   "fieldname": "region",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Region"
+  },
+  {
+   "fieldname": "region_code",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Region Code"
+  },
+  {
+   "fieldname": "country",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Country"
+  },
+  {
+   "fieldname": "country_code",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Country Code"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-14 05:33:06.444710",
+ "modified_by": "Administrator",
+ "module": "ERPNext Integrations",
+ "name": "TaxJar Nexus",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py
new file mode 100644
index 0000000..c24aa8c
--- /dev/null
+++ b/erpnext/erpnext_integrations/doctype/taxjar_nexus/taxjar_nexus.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TaxJarNexus(Document):
+	pass
diff --git a/erpnext/regional/united_states/product_tax_category_data.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/product_tax_category_data.json
similarity index 100%
rename from erpnext/regional/united_states/product_tax_category_data.json
rename to erpnext/erpnext_integrations/doctype/taxjar_settings/product_tax_category_data.json
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
index 62d5709..d495989 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js
@@ -5,5 +5,16 @@
 	is_sandbox: (frm) => {
 		frm.toggle_reqd("api_key", !frm.doc.is_sandbox);
 		frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox);
-	}
+	},
+
+	refresh: (frm) => {
+		frm.add_custom_button(__('Update Nexus List'), function() {
+			frm.call({
+				doc: frm.doc,
+				method: 'update_nexus_list'
+			});
+		});
+	},
+
+
 });
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
index c0d60f7..2d17f2e 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json
@@ -6,8 +6,8 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "is_sandbox",
   "taxjar_calculate_tax",
+  "is_sandbox",
   "taxjar_create_transactions",
   "credentials",
   "api_key",
@@ -16,7 +16,10 @@
   "configuration",
   "tax_account_head",
   "configuration_cb",
-  "shipping_account_head"
+  "shipping_account_head",
+  "section_break_12",
+  "nexus_address",
+  "nexus"
  ],
  "fields": [
   {
@@ -54,6 +57,7 @@
   },
   {
    "default": "0",
+   "depends_on": "taxjar_calculate_tax",
    "fieldname": "is_sandbox",
    "fieldtype": "Check",
    "label": "Sandbox Mode"
@@ -69,6 +73,7 @@
   },
   {
    "default": "0",
+   "depends_on": "taxjar_calculate_tax",
    "fieldname": "taxjar_create_transactions",
    "fieldtype": "Check",
    "label": "Create TaxJar Transaction"
@@ -82,11 +87,28 @@
   {
    "fieldname": "cb_keys",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_12",
+   "fieldtype": "Section Break",
+   "label": "Nexus List"
+  },
+  {
+   "fieldname": "nexus_address",
+   "fieldtype": "HTML",
+   "label": "Nexus Address"
+  },
+  {
+   "fieldname": "nexus",
+   "fieldtype": "Table",
+   "label": "Nexus",
+   "options": "TaxJar Nexus",
+   "read_only": 1
   }
  ],
  "issingle": 1,
  "links": [],
- "modified": "2020-04-30 04:38:03.311089",
+ "modified": "2021-10-06 10:59:13.475442",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "TaxJar Settings",
diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
index 9dd4817..f430a9e 100644
--- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
+++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py
@@ -4,9 +4,98 @@
 
 from __future__ import unicode_literals
 
-# import frappe
+import json
+import os
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 from frappe.model.document import Document
+from frappe.permissions import add_permission, update_permission_property
+
+from erpnext.erpnext_integrations.taxjar_integration import get_client
 
 
 class TaxJarSettings(Document):
-	pass
+
+	def on_update(self):
+		TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+		TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
+		TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
+
+		fields_already_exist = frappe.db.exists('Custom Field', {'dt': ('in', ['Item','Sales Invoice Item']), 'fieldname':'product_tax_category'})
+		fields_hidden = frappe.get_value('Custom Field', {'dt': ('in', ['Sales Invoice Item'])}, 'hidden')
+
+		if (TAXJAR_CREATE_TRANSACTIONS or TAXJAR_CALCULATE_TAX or TAXJAR_SANDBOX_MODE):
+			if not fields_already_exist:
+				add_product_tax_categories()
+				make_custom_fields()
+				add_permissions()
+				frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
+
+			elif fields_already_exist and fields_hidden:
+				toggle_tax_category_fields(hidden='0')
+
+		elif fields_already_exist:
+			toggle_tax_category_fields(hidden='1')
+
+	def validate(self):
+		self.calculate_taxes_validation_for_create_transactions()
+
+	@frappe.whitelist()
+	def update_nexus_list(self):
+		client = get_client()
+		nexus = client.nexus_regions()
+
+		new_nexus_list = [frappe._dict(address) for address in nexus]
+
+		self.set('nexus', [])
+		self.set('nexus', new_nexus_list)
+		self.save()
+
+	def calculate_taxes_validation_for_create_transactions(self):
+		if not self.taxjar_calculate_tax and (self.taxjar_create_transactions or self.is_sandbox):
+			frappe.throw(frappe._('Before enabling <b>Create Transaction</b> or <b>Sandbox Mode</b>, you need to check the <b>Enable Tax Calculation</b> box'))
+
+
+def toggle_tax_category_fields(hidden):
+	frappe.set_value('Custom Field', {'dt':'Sales Invoice Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+	frappe.set_value('Custom Field', {'dt':'Item', 'fieldname':'product_tax_category'}, 'hidden', hidden)
+
+
+def add_product_tax_categories():
+	with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
+		tax_categories = json.loads(f.read())
+	create_tax_categories(tax_categories['categories'])
+
+def create_tax_categories(data):
+	for d in data:
+		if not frappe.db.exists('Product Tax Category',{'product_tax_code':d.get('product_tax_code')}):
+			tax_category = frappe.new_doc('Product Tax Category')
+			tax_category.description = d.get("description")
+			tax_category.product_tax_code = d.get("product_tax_code")
+			tax_category.category_name = d.get("name")
+			tax_category.db_insert()
+
+def make_custom_fields(update=True):
+	custom_fields = {
+		'Sales Invoice Item': [
+			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
+				label='Product Tax Category', fetch_from='item_code.product_tax_category'),
+			dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
+				label='Tax Collectable', read_only=1),
+			dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
+				label='Taxable Amount', read_only=1)
+		],
+		'Item': [
+			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
+				label='Product Tax Category')
+		]
+	}
+	create_custom_fields(custom_fields, update=update)
+
+def add_permissions():
+	doctype = "Product Tax Category"
+	for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
+		add_permission(doctype, role, 0)
+		update_permission_property(doctype, role, 0, 'write', 1)
+		update_permission_property(doctype, role, 0, 'create', 1)
diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py
index 870a4ef..2a7243c 100644
--- a/erpnext/erpnext_integrations/taxjar_integration.py
+++ b/erpnext/erpnext_integrations/taxjar_integration.py
@@ -4,7 +4,7 @@
 import taxjar
 from frappe import _
 from frappe.contacts.doctype.address.address import get_company_address
-from frappe.utils import cint
+from frappe.utils import cint, flt
 
 from erpnext import get_default_company
 
@@ -103,7 +103,7 @@
 
 	shipping = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == SHIP_ACCOUNT_HEAD])
 
-	line_items = [get_line_item_dict(item) for item in doc.items]
+	line_items = [get_line_item_dict(item, doc.docstatus) for item in doc.items]
 
 	if from_shipping_state not in SUPPORTED_STATE_CODES:
 		from_shipping_state = get_state_code(from_address, 'Company')
@@ -139,14 +139,21 @@
 
 	return state_code
 
-def get_line_item_dict(item):
-	return dict(
+def get_line_item_dict(item, docstatus):
+	tax_dict = dict(
 		id = item.get('idx'),
 		quantity = item.get('qty'),
 		unit_price = item.get('rate'),
 		product_tax_code = item.get('product_tax_category')
 	)
 
+	if docstatus == 1:
+		tax_dict.update({
+			'sales_tax':item.get('tax_collectable')
+		})
+
+	return tax_dict
+
 def set_sales_tax(doc, method):
 	if not TAXJAR_CALCULATE_TAX:
 		return
@@ -164,6 +171,9 @@
 		setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD])
 		return
 
+	# check if delivering within a nexus
+	check_for_nexus(doc, tax_dict)
+
 	tax_data = validate_tax_request(tax_dict)
 	if tax_data is not None:
 		if not tax_data.amount_to_collect:
@@ -191,6 +201,17 @@
 
 			doc.run_method("calculate_taxes_and_totals")
 
+def check_for_nexus(doc, tax_dict):
+	if not frappe.db.get_value('TaxJar Nexus', {'region_code': tax_dict["to_state"]}):
+		for item in doc.get("items"):
+			item.tax_collectable = flt(0)
+			item.taxable_amount = flt(0)
+
+		for tax in doc.taxes:
+			if tax.account_head == TAX_ACCOUNT_HEAD:
+				doc.taxes.remove(tax)
+		return
+
 def check_sales_tax_exemption(doc):
 	# if the party is exempt from sales tax, then set all tax account heads to zero
 	sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
index 9f9204a..8e4f927 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Marketplace\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payments\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
  "creation": "2020-08-20 19:30:48.138801",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "integration",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "ERPNext Integrations",
  "links": [
   {
@@ -119,15 +112,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:58.740246",
+ "modified": "2021-08-05 12:15:58.740247",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "ERPNext Integrations",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
index fd4afb8..5fe5afa 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Integrations Settings\", \"col\": 4}}]",
  "creation": "2020-07-31 10:38:54.021237",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "setting",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "ERPNext Integrations Settings",
  "links": [
   {
@@ -81,15 +74,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:58.951704",
+ "modified": "2021-08-05 12:15:58.951705",
  "modified_by": "Administrator",
  "module": "ERPNext Integrations",
  "name": "ERPNext Integrations Settings",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d708f92..05f07f5 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -250,6 +250,7 @@
 		"validate": "erpnext.regional.india.utils.validate_tax_category"
 	},
 	"Sales Invoice": {
+		"after_insert": "erpnext.regional.saudi_arabia.utils.create_qr_code",
 		"on_submit": [
 			"erpnext.regional.create_transaction_log",
 			"erpnext.regional.italy.utils.sales_invoice_on_submit",
@@ -259,7 +260,10 @@
 			"erpnext.regional.italy.utils.sales_invoice_on_cancel",
 			"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
 		],
-		"on_trash": "erpnext.regional.check_deletion_permission",
+		"on_trash": [
+			"erpnext.regional.check_deletion_permission",
+			"erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
+		],
 		"validate": [
 			"erpnext.regional.india.utils.validate_document_name",
 			"erpnext.regional.india.utils.update_taxable_values"
diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
index bed12e3..8a23682 100644
--- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
@@ -74,7 +74,6 @@
 			from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \
 			where q.name = r.parent""", as_dict=1)
 
-		frappe.db.commit()
 
 	def setup_groups(self, hour=None):
 		# setup email to trigger at this hour
diff --git a/erpnext/hr/doctype/employee/employee_reminders.py b/erpnext/hr/doctype/employee/employee_reminders.py
index 216d8f6..559bd39 100644
--- a/erpnext/hr/doctype/employee/employee_reminders.py
+++ b/erpnext/hr/doctype/employee/employee_reminders.py
@@ -157,6 +157,8 @@
 			AND
 				MONTH({condition_column}) = MONTH(%(today)s)
 			AND
+				YEAR({condition_column}) < YEAR(%(today)s)
+			AND
 				`status` = 'Active'
 		""",
 		"postgres": f"""
@@ -167,6 +169,8 @@
 			AND
 				DATE_PART('month', {condition_column}) = date_part('month', %(today)s)
 			AND
+				DATE_PART('year', {condition_column}) < date_part('year', %(today)s)
+			AND
 				"status" = 'Active'
 		""",
 	}, dict(today=today(), condition_column=condition_column), as_dict=1)
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index 8d6dfa2..8a2da08 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -55,6 +55,7 @@
 			"email": user,
 			"first_name": user,
 			"new_password": "password",
+			"send_welcome_email": 0,
 			"roles": [{"doctype": "Has Role", "role": "Employee"}]
 		}).insert()
 
diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
index 164d48b..b051752 100644
--- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py
+++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py
@@ -9,7 +9,7 @@
 from frappe.model.document import Document
 from frappe.utils import getdate
 
-from erpnext.hr.utils import update_employee, validate_active_employee
+from erpnext.hr.utils import update_employee_work_history, validate_active_employee
 
 
 class EmployeePromotion(Document):
@@ -23,10 +23,10 @@
 
 	def on_submit(self):
 		employee = frappe.get_doc("Employee", self.employee)
-		employee = update_employee(employee, self.promotion_details, date=self.promotion_date)
+		employee = update_employee_work_history(employee, self.promotion_details, date=self.promotion_date)
 		employee.save()
 
 	def on_cancel(self):
 		employee = frappe.get_doc("Employee", self.employee)
-		employee = update_employee(employee, self.promotion_details, cancel=True)
+		employee = update_employee_work_history(employee, self.promotion_details, cancel=True)
 		employee.save()
diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
index b1f6609..29d93f3 100644
--- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py
@@ -9,7 +9,7 @@
 from frappe.model.document import Document
 from frappe.utils import getdate
 
-from erpnext.hr.utils import update_employee
+from erpnext.hr.utils import update_employee_work_history
 
 
 class EmployeeTransfer(Document):
@@ -24,7 +24,7 @@
 			new_employee = frappe.copy_doc(employee)
 			new_employee.name = None
 			new_employee.employee_number = None
-			new_employee = update_employee(new_employee, self.transfer_details, date=self.transfer_date)
+			new_employee = update_employee_work_history(new_employee, self.transfer_details, date=self.transfer_date)
 			if self.new_company and self.company != self.new_company:
 				new_employee.internal_work_history = []
 				new_employee.date_of_joining = self.transfer_date
@@ -39,7 +39,7 @@
 			employee.db_set("relieving_date", self.transfer_date)
 			employee.db_set("status", "Left")
 		else:
-			employee = update_employee(employee, self.transfer_details, date=self.transfer_date)
+			employee = update_employee_work_history(employee, self.transfer_details, date=self.transfer_date)
 			if self.new_company and self.company != self.new_company:
 				employee.company = self.new_company
 				employee.date_of_joining = self.transfer_date
@@ -56,7 +56,7 @@
 			employee.status = "Active"
 			employee.relieving_date = ''
 		else:
-			employee = update_employee(employee, self.transfer_details, cancel=True)
+			employee = update_employee_work_history(employee, self.transfer_details, date=self.transfer_date, cancel=True)
 		if self.new_company != self.company:
 			employee.company = self.company
 		employee.save()
diff --git a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
index ad2f3ad..c0440d0 100644
--- a/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
+++ b/erpnext/hr/doctype/employee_transfer/test_employee_transfer.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 
 import unittest
+from datetime import date
 
 import frappe
 from frappe.utils import add_days, getdate
@@ -15,7 +16,12 @@
 	def setUp(self):
 		make_employee("employee2@transfers.com")
 		make_employee("employee3@transfers.com")
-		frappe.db.sql("""delete from `tabEmployee Transfer`""")
+		create_company()
+		create_employee()
+		create_employee_transfer()
+
+	def tearDown(self):
+		frappe.db.rollback()
 
 	def test_submit_before_transfer_date(self):
 		transfer_obj = frappe.get_doc({
@@ -57,3 +63,77 @@
 		self.assertTrue(transfer.new_employee_id)
 		self.assertEqual(frappe.get_value("Employee", transfer.new_employee_id, "status"), "Active")
 		self.assertEqual(frappe.get_value("Employee", transfer.employee, "status"), "Left")
+
+	def test_employee_history(self):
+		name = frappe.get_value("Employee", {"first_name": "John", "company": "Test Company"}, "name")
+		doc = frappe.get_doc("Employee",name)
+		count = 0
+		department = ["Accounts - TC", "Management - TC"]
+		designation = ["Accountant", "Manager"]
+		dt = [getdate("01-10-2021"), date.today()]
+
+		for data in doc.internal_work_history:
+			self.assertEqual(data.department, department[count])
+			self.assertEqual(data.designation, designation[count])
+			self.assertEqual(data.from_date, dt[count])
+			count = count + 1
+
+		data = frappe.db.get_list("Employee Transfer", filters={"employee":name}, fields=["*"])
+		doc = frappe.get_doc("Employee Transfer", data[0]["name"])
+		doc.cancel()
+		employee_doc = frappe.get_doc("Employee",name)
+
+		for data in employee_doc.internal_work_history:
+			self.assertEqual(data.designation, designation[0])
+			self.assertEqual(data.department, department[0])
+			self.assertEqual(data.from_date, dt[0])
+
+def create_employee():
+	doc = frappe.get_doc({
+			"doctype": "Employee",
+			"first_name": "John",
+			"company": "Test Company",
+			"gender": "Male",
+			"date_of_birth": getdate("30-09-1980"),
+			"date_of_joining": getdate("01-10-2021"),
+			"department": "Accounts - TC",
+			"designation": "Accountant"
+	})
+
+	doc.save()
+
+def create_company():
+	exists = frappe.db.exists("Company", "Test Company")
+	if not exists:
+		doc = frappe.get_doc({
+				"doctype": "Company",
+				"company_name": "Test Company",
+				"default_currency": "INR",
+				"country": "India"
+		})
+
+		doc.save()
+
+def create_employee_transfer():
+	doc = frappe.get_doc({
+		"doctype": "Employee Transfer",
+		"employee": frappe.get_value("Employee", {"first_name": "John", "company": "Test Company"}, "name"),
+		"transfer_date": date.today(),
+		"transfer_details": [
+			{
+				"property": "Designation",
+				"current": "Accountant",
+				"new": "Manager",
+				"fieldname": "designation"
+			},
+			{
+				"property": "Department",
+				"current": "Accounts - TC",
+				"new": "Management - TC",
+				"fieldname": "department"
+			}
+		]
+	})
+
+	doc.save()
+	doc.submit()
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js
index 3c4c672..218e97d 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.js
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.js
@@ -10,6 +10,26 @@
 	},
 	company: function(frm) {
 		erpnext.accounts.dimensions.update_dimension(frm, frm.doctype);
+		var expenses = frm.doc.expenses;
+		for (var i = 0; i < expenses.length; i++) {
+			var expense = expenses[i];
+			if (!expense.expense_type) {
+				continue;
+			}
+			frappe.call({
+				method: "erpnext.hr.doctype.expense_claim.expense_claim.get_expense_claim_account_and_cost_center",
+				args: {
+					"expense_claim_type": expense.expense_type,
+					"company": frm.doc.company
+				},
+				callback: function(r) {
+					if (r.message) {
+						expense.default_account = r.message.account;
+						expense.cost_center = r.message.cost_center;
+					}
+				}
+			});
+		}
 	},
 });
 
diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
index 9cb65f7..941fd58 100644
--- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py
@@ -176,7 +176,7 @@
 	account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
 	return {'taxes':[{
 		"account_head": account,
-		"rate": 0,
+		"rate": 9,
 		"description": "CGST",
 		"tax_amount": 10,
 		"total": 210
diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
index 020457d..2f7b8fc 100644
--- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
+++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
@@ -56,8 +56,6 @@
   },
   {
    "columns": 2,
-   "fetch_from": "account_head.tax_rate",
-   "fetch_if_empty": 1,
    "fieldname": "rate",
    "fieldtype": "Float",
    "in_list_view": 1,
@@ -102,7 +100,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2020-09-23 20:27:36.027728",
+ "modified": "2021-10-26 20:27:36.027728",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Expense Taxes and Charges",
@@ -111,4 +109,4 @@
  "sort_field": "modified",
  "sort_order": "ASC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/hr/doctype/holiday_list/holiday_list.py b/erpnext/hr/doctype/holiday_list/holiday_list.py
index f46f14d..7d1b991 100644
--- a/erpnext/hr/doctype/holiday_list/holiday_list.py
+++ b/erpnext/hr/doctype/holiday_list/holiday_list.py
@@ -1,4 +1,3 @@
-
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
 
@@ -94,9 +93,11 @@
 		update={"allDay": 1})
 
 
-def is_holiday(holiday_list, date=today()):
+def is_holiday(holiday_list, date=None):
 	"""Returns true if the given date is a holiday in the given holiday list
 	"""
+	if date is None:
+		date = today()
 	if holiday_list:
 		return bool(frappe.get_all('Holiday List',
 			dict(name=holiday_list, holiday_date=date)))
diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
index 69af5c5..05b74a0 100644
--- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py
+++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py
@@ -139,7 +139,7 @@
 	return shift_timing_map
 
 
-def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None):
+def get_employee_shift(employee, for_date=None, consider_default_shift=False, next_shift_direction=None):
 	"""Returns a Shift Type for the given employee on the given date. (excluding the holidays)
 
 	:param employee: Employee for which shift is required.
@@ -147,6 +147,8 @@
 	:param consider_default_shift: If set to true, default shift is taken when no shift assignment is found.
 	:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
 	"""
+	if for_date is None:
+		for_date = nowdate()
 	default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
 	shift_type_name = None
 	shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date'])
@@ -200,9 +202,11 @@
 	return get_shift_details(shift_type_name, for_date)
 
 
-def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_default_shift=False):
+def get_employee_shift_timings(employee, for_timestamp=None, consider_default_shift=False):
 	"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
 	"""
+	if for_timestamp is None:
+		for_timestamp = now_datetime()
 	# write and verify a test case for midnight shift.
 	prev_shift = curr_shift = next_shift = None
 	curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
@@ -220,7 +224,7 @@
 	return prev_shift, curr_shift, next_shift
 
 
-def get_shift_details(shift_type_name, for_date=nowdate()):
+def get_shift_details(shift_type_name, for_date=None):
 	"""Returns Shift Details which contain some additional information as described below.
 	'shift_details' contains the following keys:
 		'shift_type' - Object of DocType Shift Type,
@@ -234,6 +238,8 @@
 	"""
 	if not shift_type_name:
 		return None
+	if not for_date:
+		for_date = nowdate()
 	shift_type = frappe.get_doc('Shift Type', shift_type_name)
 	start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time
 	for_date = for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date
diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py
index e53373d..7a35b28 100644
--- a/erpnext/hr/doctype/shift_type/shift_type.py
+++ b/erpnext/hr/doctype/shift_type/shift_type.py
@@ -97,7 +97,7 @@
 		assigned_employees = [x[0] for x in assigned_employees]
 
 		if consider_default_shift:
-			filters = {'default_shift': self.name}
+			filters = {'default_shift': self.name, 'status': ['!=', 'Inactive']}
 			default_shift_employees = frappe.get_all('Employee', 'name', filters, as_list=True)
 			default_shift_employees = [x[0] for x in default_shift_employees]
 			return list(set(assigned_employees+default_shift_employees))
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 57a92b0..93cd4e1 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -155,7 +155,11 @@
 	return employee_counts
 
 @frappe.whitelist()
-def get_active_staffing_plan_details(company, designation, from_date=getdate(nowdate()), to_date=getdate(nowdate())):
+def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None):
+	if from_date is None:
+		from_date = getdate(nowdate())
+	if to_date is None:
+		to_date = getdate(nowdate())
 	if not company or not designation:
 		frappe.throw(_("Please select Company and Designation"))
 
diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
index 6bca136..d463b9b 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -182,10 +182,11 @@
 	records= frappe.db.sql("""
 		SELECT
 			employee, leave_type, from_date, to_date, leaves, transaction_name,
-			is_carry_forward, is_expired
+			transaction_type, is_carry_forward, is_expired
 		FROM `tabLeave Ledger Entry`
 		WHERE employee=%(employee)s AND leave_type=%(leave_type)s
 			AND docstatus=1
+			AND transaction_type = 'Leave Allocation'
 			AND (from_date between %(from_date)s AND %(to_date)s
 				OR to_date between %(from_date)s AND %(to_date)s
 				OR (from_date < %(from_date)s AND to_date > %(to_date)s))
diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py
index b6f4cad..0febce1 100644
--- a/erpnext/hr/utils.py
+++ b/erpnext/hr/utils.py
@@ -29,7 +29,15 @@
 	if doc.employee and not doc.employee_name:
 		doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name")
 
-def update_employee(employee, details, date=None, cancel=False):
+def update_employee_work_history(employee, details, date=None, cancel=False):
+	if not employee.internal_work_history and not cancel:
+		employee.append("internal_work_history", {
+			"branch": employee.branch,
+			"designation": employee.designation,
+			"department": employee.department,
+			"from_date": employee.date_of_joining
+		})
+
 	internal_work_history = {}
 	for item in details:
 		field = frappe.get_meta("Employee").get_field(item.fieldname)
@@ -44,11 +52,35 @@
 		setattr(employee, item.fieldname, new_data)
 		if item.fieldname in ["department", "designation", "branch"]:
 			internal_work_history[item.fieldname] = item.new
+
 	if internal_work_history and not cancel:
 		internal_work_history["from_date"] = date
 		employee.append("internal_work_history", internal_work_history)
+
+	if cancel:
+		delete_employee_work_history(details, employee, date)
+
 	return employee
 
+def delete_employee_work_history(details, employee, date):
+	filters = {}
+	for d in details:
+		for history in employee.internal_work_history:
+			if d.property == "Department" and history.department == d.new:
+				department = d.new
+				filters["department"] = department
+			if d.property == "Designation" and history.designation == d.new:
+				designation = d.new
+				filters["designation"] = designation
+			if d.property == "Branch" and history.branch == d.new:
+				branch = d.new
+				filters["branch"] = branch
+			if date and date == history.from_date:
+				filters["from_date"] = date
+	if filters:
+		frappe.db.delete("Employee Internal Work History", filters)
+
+
 @frappe.whitelist()
 def get_employee_fields_label():
 	fields = []
diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json
index 9c5d0c1..7408d63 100644
--- a/erpnext/hr/workspace/hr/hr.json
+++ b/erpnext/hr/workspace/hr/hr.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Outgoing Salary",
@@ -8,18 +7,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Human Resource\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Employee\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Leave Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Job Applicant\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee Lifecycle\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Shift Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Leaves\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Expense Claims\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fleet Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Recruitment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loans\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Training\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Performance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
  "creation": "2020-03-02 15:48:58.322521",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "hr",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "HR",
  "links": [
   {
@@ -942,15 +935,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-31 12:18:59.842918",
+ "modified": "2021-08-31 12:18:59.842919",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "HR",
- "onboarding": "Human Resource",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index c9f23ca..5979992 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -334,7 +334,6 @@
   },
   {
    "depends_on": "eval:doc.is_secured_loan",
-   "fetch_from": "loan_application.maximum_loan_amount",
    "fieldname": "maximum_loan_amount",
    "fieldtype": "Currency",
    "label": "Maximum Loan Amount",
@@ -360,7 +359,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-04-19 18:10:32.360818",
+ "modified": "2021-10-12 18:10:32.360818",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index 7dbd422..0f2c3cf 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -137,16 +137,23 @@
 			frappe.throw(_("Loan amount is mandatory"))
 
 	def link_loan_security_pledge(self):
-		if self.is_secured_loan:
-			loan_security_pledge = frappe.db.get_value('Loan Security Pledge', {'loan_application': self.loan_application},
-				'name')
+		if self.is_secured_loan and self.loan_application:
+			maximum_loan_value = frappe.db.get_value('Loan Security Pledge',
+				{
+					'loan_application': self.loan_application,
+					'status': 'Requested'
+				},
+				'sum(maximum_loan_value)'
+			)
 
-			if loan_security_pledge:
-				frappe.db.set_value('Loan Security Pledge', loan_security_pledge, {
-					'loan': self.name,
-					'status': 'Pledged',
-					'pledge_time': now_datetime()
-				})
+			if maximum_loan_value:
+				frappe.db.sql("""
+					UPDATE `tabLoan Security Pledge`
+					SET loan = %s, pledge_time = %s, status = 'Pledged'
+					WHERE status = 'Requested' and loan_application = %s
+				""", (self.name, now_datetime(), self.loan_application))
+
+				self.db_set('maximum_loan_amount', maximum_loan_value)
 
 	def unlink_loan_security_pledge(self):
 		pledges = frappe.get_all('Loan Security Pledge', fields=['name'], filters={'loan': self.name})
diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py
index e492920..ede0467 100644
--- a/erpnext/loan_management/doctype/loan_application/loan_application.py
+++ b/erpnext/loan_management/doctype/loan_application/loan_application.py
@@ -130,10 +130,11 @@
 def create_loan(source_name, target_doc=None, submit=0):
 	def update_accounts(source_doc, target_doc, source_parent):
 		account_details = frappe.get_all("Loan Type",
-		 fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
-		 filters = {'name': source_doc.loan_type}
-		)[0]
+			fields=["mode_of_payment", "payment_account","loan_account", "interest_income_account", "penalty_income_account"],
+			filters = {'name': source_doc.loan_type})[0]
 
+		if source_doc.is_secured_loan:
+			target_doc.maximum_loan_amount = 0
 
 		target_doc.mode_of_payment = account_details.mode_of_payment
 		target_doc.payment_account = account_details.payment_account
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 6d9d4f4..99f0d25 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -198,7 +198,7 @@
 		security_value = get_total_pledged_security_value(loan)
 
 	if loan_details.is_secured_loan and not on_current_security_price:
-		security_value = flt(loan_details.maximum_loan_amount)
+		security_value = get_maximum_amount_as_per_pledged_security(loan)
 
 	if not security_value and not loan_details.is_secured_loan:
 		security_value = flt(loan_details.loan_amount)
@@ -209,3 +209,6 @@
 		disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount
 
 	return disbursal_amount
+
+def get_maximum_amount_as_per_pledged_security(loan):
+	return flt(frappe.db.get_value('Loan Security Pledge', {'loan': loan}, 'sum(maximum_loan_value)'))
diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
index 13b7357..40bb581 100644
--- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
+++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
@@ -411,7 +411,7 @@
 		if due_date and not final_due_date:
 			final_due_date = add_days(due_date, loan_type_details.grace_period_in_days)
 
-	if against_loan_doc.status in ('Disbursed', 'Loan Closure Requested', 'Closed'):
+	if against_loan_doc.status in ('Disbursed', 'Closed') or against_loan_doc.disbursed_amount >= against_loan_doc.loan_amount:
 		pending_principal_amount = against_loan_doc.total_payment - against_loan_doc.total_principal_paid \
 			- against_loan_doc.total_interest_payable - against_loan_doc.written_off_amount
 	else:
diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json
index ca528ec..7deee0d 100644
--- a/erpnext/loan_management/workspace/loan_management/loan_management.json
+++ b/erpnext/loan_management/workspace/loan_management/loan_management.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Processes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Disbursement and Repayment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Security\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
  "creation": "2020-03-12 16:35:55.299820",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "loan",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Loans",
  "links": [
   {
@@ -245,15 +238,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:18:13.350904",
+ "modified": "2021-08-05 12:18:13.350905",
  "modified_by": "Administrator",
  "module": "Loan Management",
  "name": "Loans",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 0bf5aea..adb57f9 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -47,7 +47,7 @@
 			"Yearly": 365
 		}
 		for item in self.items:
-			if item.periodicity and item.start_date:
+			if item.periodicity and item.periodicity != "Random" and item.start_date:
 				if not item.end_date:
 					if item.no_of_visits:
 						item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
@@ -199,12 +199,16 @@
 				if chk:
 					throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
 
+	def validate_no_of_visits(self):
+		return len(self.schedules) != sum(d.no_of_visits for d in self.items)
+
 	def validate(self):
 		self.validate_end_date_visits()
 		self.validate_maintenance_detail()
 		self.validate_dates_with_periodicity()
 		self.validate_sales_order()
-		self.generate_schedule()
+		if not self.schedules or self.validate_no_of_visits():
+			self.generate_schedule()
 
 	def on_update(self):
 		frappe.db.set(self, 'status', 'Draft')
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 7cfec97..232e3a0 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1133,8 +1133,7 @@
 			query_filters["has_variants"] = 0
 
 	if filters and filters.get("is_stock_item"):
-		or_cond_filters["is_stock_item"] = 1
-		or_cond_filters["has_variants"] = 1
+		query_filters["is_stock_item"] = 1
 
 	return frappe.get_list("Item",
 		fields = fields, filters=query_filters,
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 706ea26..4c03230 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -4,13 +4,14 @@
 
 import unittest
 from collections import deque
+from functools import partial
 
 import frappe
 from frappe.test_runner import make_test_records
 from frappe.utils import cstr, flt
 
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
-from erpnext.manufacturing.doctype.bom.bom import make_variant_bom
+from erpnext.manufacturing.doctype.bom.bom import item_query, make_variant_bom
 from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
@@ -375,6 +376,16 @@
 		# FG Items in Scrap/Loss Table should have Is Process Loss set
 		self.assertRaises(frappe.ValidationError, bom_doc.submit)
 
+	def test_bom_item_query(self):
+		query = partial(item_query, doctype="Item", txt="", searchfield="name", start=0, page_len=20, filters={"is_stock_item": 1})
+
+		test_items = query(txt="_Test")
+		filtered = query(txt="_Test Item 2")
+
+		self.assertNotEqual(len(test_items), len(filtered), msg="Item filtering showing excessive results")
+		self.assertTrue(0 < len(filtered) <= 3, msg="Item filtering showing excessive results")
+
+
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
 
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index b9efe9b..2424ef9 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -311,7 +311,7 @@
 
 		if self.total_produced_qty > 0:
 			self.status = "In Process"
-			if self.total_produced_qty >= self.total_planned_qty:
+			if self.check_have_work_orders_completed():
 				self.status = "Completed"
 
 		if self.status != 'Completed':
@@ -424,7 +424,7 @@
 			po = frappe.new_doc('Purchase Order')
 			po.supplier = supplier
 			po.schedule_date = getdate(po_list[0].schedule_date) if po_list[0].schedule_date else nowdate()
-			po.is_subcontracted_item = 'Yes'
+			po.is_subcontracted = 'Yes'
 			for row in po_list:
 				args = {
 					'item_code': row.production_item,
@@ -575,6 +575,15 @@
 
 			self.append("sub_assembly_items", data)
 
+	def check_have_work_orders_completed(self):
+		wo_status = frappe.db.get_list(
+			"Work Order",
+			filters={"production_plan": self.name},
+			fields="status",
+			pluck="status"
+		)
+		return all(s == "Completed" for s in wo_status)
+
 @frappe.whitelist()
 def download_raw_materials(doc, warehouses=None):
 	if isinstance(doc, str):
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
index a7aec31..74bd685 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
@@ -24,7 +24,7 @@
 	}
 
 	fields = ["name", "status", "work_order", "production_item", "item_name", "posting_date",
-		"total_completed_qty", "workstation", "operation", "employee_name", "total_time_in_mins"]
+		"total_completed_qty", "workstation", "operation", "total_time_in_mins"]
 
 	for field in ["work_order", "workstation", "operation", "company"]:
 		if filters.get(field):
@@ -45,7 +45,7 @@
 	job_card_time_details = {}
 	for job_card_data in frappe.get_all("Job Card Time Log",
 		fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"],
-		filters=job_card_time_filter, group_by="parent", debug=1):
+		filters=job_card_time_filter, group_by="parent"):
 		job_card_time_details[job_card_data.parent] = job_card_data
 
 	res = []
@@ -173,12 +173,6 @@
 			"width": 110
 		},
 		{
-			"label": _("Employee Name"),
-			"fieldname": "employee_name",
-			"fieldtype": "Data",
-			"width": 110
-		},
-		{
 			"label": _("Total Completed Qty"),
 			"fieldname": "total_completed_qty",
 			"fieldtype": "Float",
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/manufacturing/report/process_loss_report/__init__.py
similarity index 100%
rename from erpnext/stock/report/process_loss_report/__init__.py
rename to erpnext/manufacturing/report/process_loss_report/__init__.py
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.js b/erpnext/manufacturing/report/process_loss_report/process_loss_report.js
similarity index 100%
rename from erpnext/stock/report/process_loss_report/process_loss_report.js
rename to erpnext/manufacturing/report/process_loss_report/process_loss_report.js
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.json b/erpnext/manufacturing/report/process_loss_report/process_loss_report.json
similarity index 83%
rename from erpnext/stock/report/process_loss_report/process_loss_report.json
rename to erpnext/manufacturing/report/process_loss_report/process_loss_report.json
index afe4aff..7d3d13d 100644
--- a/erpnext/stock/report/process_loss_report/process_loss_report.json
+++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.json
@@ -9,9 +9,9 @@
  "filters": [],
  "idx": 0,
  "is_standard": "Yes",
- "modified": "2021-08-24 16:38:15.233395",
+ "modified": "2021-10-20 22:03:57.606612",
  "modified_by": "Administrator",
- "module": "Stock",
+ "module": "Manufacturing",
  "name": "Process Loss Report",
  "owner": "Administrator",
  "prepared_report": 0,
@@ -21,9 +21,6 @@
  "roles": [
   {
    "role": "Manufacturing User"
-  },
-  {
-   "role": "Stock User"
   }
  ]
 }
\ No newline at end of file
diff --git a/erpnext/stock/report/process_loss_report/process_loss_report.py b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
similarity index 97%
rename from erpnext/stock/report/process_loss_report/process_loss_report.py
rename to erpnext/manufacturing/report/process_loss_report/process_loss_report.py
index 499c49f..9b544da 100644
--- a/erpnext/stock/report/process_loss_report/process_loss_report.py
+++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.py
@@ -111,7 +111,7 @@
 			{work_order_filter}
 		GROUP BY
 			se.work_order
-	""".format(**query_args), query_args, as_dict=1, debug=1)
+	""".format(**query_args), query_args, as_dict=1)
 
 def update_data_with_total_pl_value(data: Data) -> None:
 	for row in data:
diff --git a/erpnext/manufacturing/report/test_reports.py b/erpnext/manufacturing/report/test_reports.py
new file mode 100644
index 0000000..1de4726
--- /dev/null
+++ b/erpnext/manufacturing/report/test_reports.py
@@ -0,0 +1,64 @@
+import unittest
+from typing import List, Tuple
+
+import frappe
+
+from erpnext.tests.utils import ReportFilters, ReportName, execute_script_report
+
+DEFAULT_FILTERS = {
+	"company": "_Test Company",
+	"from_date": "2010-01-01",
+	"to_date": "2030-01-01",
+	"warehouse": "_Test Warehouse - _TC",
+}
+
+
+REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [
+	("BOM Explorer", {"bom": frappe.get_last_doc("BOM").name}),
+	("BOM Operations Time", {}),
+	("BOM Stock Calculated", {"bom": frappe.get_last_doc("BOM").name, "qty_to_make": 2}),
+	("BOM Stock Report", {"bom": frappe.get_last_doc("BOM").name, "qty_to_produce": 2}),
+	("Cost of Poor Quality Report", {}),
+	("Downtime Analysis", {}),
+	(
+		"Exponential Smoothing Forecasting",
+		{
+			"based_on_document": "Sales Order",
+			"based_on_field": "Qty",
+			"no_of_years": 3,
+			"periodicity": "Yearly",
+			"smoothing_constant": 0.3,
+		},
+	),
+	("Job Card Summary", {"fiscal_year": "2021-2022"}),
+	("Production Analytics", {"range": "Monthly"}),
+	("Quality Inspection Summary", {}),
+	("Process Loss Report", {}),
+	("Work Order Stock Report", {}),
+	("Work Order Summary", {"fiscal_year": "2021-2022", "age": 0}),
+]
+
+
+if frappe.db.a_row_exists("Production Plan"):
+	REPORT_FILTER_TEST_CASES.append(
+		("Production Plan Summary", {"production_plan": frappe.get_last_doc("Production Plan").name})
+	)
+
+OPTIONAL_FILTERS = {
+	"warehouse": "_Test Warehouse - _TC",
+	"item": "_Test Item",
+	"item_group": "_Test Item Group",
+}
+
+
+class TestManufacturingReports(unittest.TestCase):
+	def test_execute_all_manufacturing_reports(self):
+		"""Test that all script report in manufacturing modules are executable with supported filters"""
+		for report, filter in REPORT_FILTER_TEST_CASES:
+			execute_script_report(
+				report_name=report,
+				module="Manufacturing",
+				filters=filter,
+				default_filters=DEFAULT_FILTERS,
+				optional_filters=OPTIONAL_FILTERS if filter.get("_optional") else None,
+			)
diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
index 84eabcd..cfa80f8 100644
--- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
+++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Produced Quantity"
@@ -7,18 +6,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Manufacturing\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Plan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Forecasting\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM Stock Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Planning Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Production\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bill of Materials\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]",
  "creation": "2020-03-02 17:11:37.032604",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "organization",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Manufacturing",
  "links": [
   {
@@ -304,15 +297,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:00.825741",
+ "modified": "2021-08-05 12:16:00.825742",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Manufacturing",
- "onboarding": "Manufacturing",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "Manufacturing",
  "roles": [],
diff --git a/erpnext/non_profit/workspace/non_profit/non_profit.json b/erpnext/non_profit/workspace/non_profit/non_profit.json
index e6d4445..ba2f919 100644
--- a/erpnext/non_profit/workspace/non_profit/non_profit.json
+++ b/erpnext/non_profit/workspace/non_profit/non_profit.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Member\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Profit Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Membership\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter Member\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Grant Application\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Membership\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Volunteer\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Donation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tax Exemption Certification (India)\", \"col\": 4}}]",
  "creation": "2020-03-02 17:23:47.811421",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "non-profit",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Non Profit",
  "links": [
   {
@@ -238,15 +231,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:01.146206",
+ "modified": "2021-08-05 12:16:01.146207",
  "modified_by": "Administrator",
  "module": "Non Profit",
  "name": "Non Profit",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "Non Profit",
  "roles": [],
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 22a6313..1dac50c 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -246,7 +246,7 @@
 erpnext.patches.v12_0.add_state_code_for_ladakh
 erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
 erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
-erpnext.patches.v12_0.update_vehicle_no_reqd_condition
+erpnext.patches.v13_0.update_vehicle_no_reqd_condition
 erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
 erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
 erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
@@ -286,12 +286,15 @@
 erpnext.patches.v13_0.migrate_stripe_api
 erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
 erpnext.patches.v13_0.einvoicing_deprecation_warning
+execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings")
+execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category")
 erpnext.patches.v13_0.custom_fields_for_taxjar_integration
 erpnext.patches.v14_0.delete_einvoicing_doctypes
 erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
 erpnext.patches.v13_0.validate_options_for_data_field
 erpnext.patches.v13_0.create_gst_payment_entry_fields
 erpnext.patches.v14_0.delete_shopify_doctypes
+erpnext.patches.v13_0.fix_invoice_statuses
 erpnext.patches.v13_0.replace_supplier_item_group_with_party_specific_item
 erpnext.patches.v13_0.update_dates_in_tax_withholding_category
 erpnext.patches.v14_0.update_opportunity_currency_fields
@@ -299,7 +302,13 @@
 erpnext.patches.v13_0.create_accounting_dimensions_in_pos_doctypes
 erpnext.patches.v13_0.trim_sales_invoice_custom_field_length
 erpnext.patches.v13_0.create_custom_field_for_finance_book
-erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries
+erpnext.patches.v13_0.modify_invalid_gain_loss_gl_entries #2
 erpnext.patches.v13_0.fix_additional_cost_in_mfg_stock_entry
 erpnext.patches.v13_0.set_status_in_maintenance_schedule_table
-erpnext.patches.v13_0.add_default_interview_notification_templates
\ No newline at end of file
+erpnext.patches.v13_0.add_default_interview_notification_templates
+erpnext.patches.v13_0.enable_scheduler_job_for_item_reposting
+erpnext.patches.v13_0.requeue_failed_reposts
+erpnext.patches.v12_0.update_production_plan_status
+erpnext.patches.v13_0.healthcare_deprecation_warning
+erpnext.patches.v14_0.delete_healthcare_doctypes
+erpnext.patches.v13_0.create_pan_field_for_india #2
diff --git a/erpnext/patches/v12_0/update_production_plan_status.py b/erpnext/patches/v12_0/update_production_plan_status.py
new file mode 100644
index 0000000..06fc503
--- /dev/null
+++ b/erpnext/patches/v12_0/update_production_plan_status.py
@@ -0,0 +1,31 @@
+# Copyright (c) 2021, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+import frappe
+
+
+def execute():
+	frappe.reload_doc("manufacturing", "doctype", "production_plan")
+	frappe.db.sql("""
+		UPDATE `tabProduction Plan` ppl
+		SET status = "Completed"
+		WHERE ppl.name IN (
+			SELECT ss.name FROM (
+				SELECT
+					(
+						count(wo.status = "Completed") =
+						count(pp.name)
+					) =
+					(
+						pp.status != "Completed"
+						AND pp.total_produced_qty >= pp.total_planned_qty
+					) AS should_set,
+					pp.name AS name
+				FROM
+					`tabWork Order` wo INNER JOIN`tabProduction Plan` pp
+					ON wo.production_plan = pp.name
+				GROUP BY pp.name
+				HAVING should_set = 1
+			) ss
+		)
+	""")
diff --git a/erpnext/patches/v13_0/create_pan_field_for_india.py b/erpnext/patches/v13_0/create_pan_field_for_india.py
new file mode 100644
index 0000000..c37651a
--- /dev/null
+++ b/erpnext/patches/v13_0/create_pan_field_for_india.py
@@ -0,0 +1,28 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+	frappe.reload_doc('buying', 'doctype', 'supplier', force=True)
+	frappe.reload_doc('selling', 'doctype', 'customer', force=True)
+
+	custom_fields = {
+		'Supplier': [
+			{
+				'fieldname': 'pan',
+				'label': 'PAN',
+				'fieldtype': 'Data',
+				'insert_after': 'supplier_type'
+			}
+		],
+		'Customer': [
+			{
+				'fieldname': 'pan',
+				'label': 'PAN',
+				'fieldtype': 'Data',
+				'insert_after': 'customer_type'
+			}
+		]
+	}
+
+	create_custom_fields(custom_fields, update=True)
diff --git a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
index eee9f11..e136d64 100644
--- a/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
+++ b/erpnext/patches/v13_0/custom_fields_for_taxjar_integration.py
@@ -3,7 +3,7 @@
 import frappe
 from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
 
-from erpnext.regional.united_states.setup import add_permissions
+from erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings import add_permissions
 
 
 def execute():
@@ -11,7 +11,12 @@
 	if not company:
 		return
 
-	frappe.reload_doc("regional", "doctype", "product_tax_category")
+	TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
+	TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
+	TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
+
+	if (not TAXJAR_CREATE_TRANSACTIONS and not TAXJAR_CALCULATE_TAX and not TAXJAR_SANDBOX_MODE):
+		return
 
 	custom_fields = {
 		'Sales Invoice Item': [
@@ -29,4 +34,4 @@
 	}
 	create_custom_fields(custom_fields, update=True)
 	add_permissions()
-	frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=True)
+	frappe.enqueue('erpnext.erpnext_integrations.doctype.taxjar_settings.taxjar_settings.add_product_tax_categories', now=True)
diff --git a/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py b/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
new file mode 100644
index 0000000..7a51b43
--- /dev/null
+++ b/erpnext/patches/v13_0/enable_scheduler_job_for_item_reposting.py
@@ -0,0 +1,8 @@
+import frappe
+
+
+def execute():
+	frappe.reload_doc('core', 'doctype', 'scheduled_job_type')
+	if frappe.db.exists('Scheduled Job Type', 'repost_item_valuation.repost_entries'):
+		frappe.db.set_value('Scheduled Job Type',
+			'repost_item_valuation.repost_entries', 'stopped', 0)
diff --git a/erpnext/patches/v13_0/fix_invoice_statuses.py b/erpnext/patches/v13_0/fix_invoice_statuses.py
new file mode 100644
index 0000000..4395757
--- /dev/null
+++ b/erpnext/patches/v13_0/fix_invoice_statuses.py
@@ -0,0 +1,113 @@
+import frappe
+from frappe.utils import flt, getdate
+
+from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
+	get_total_in_party_account_currency,
+	is_overdue,
+)
+
+TODAY = getdate()
+
+def execute():
+	# This fix is not related to Party Specific Item,
+	# but it is needed for code introduced after Party Specific Item was
+	# If your DB doesn't have this doctype yet, you should be fine
+	if not frappe.db.exists("DocType", "Party Specific Item"):
+		return
+
+	for doctype in ("Purchase Invoice", "Sales Invoice"):
+		fields = [
+			"name",
+			"status",
+			"due_date",
+			"outstanding_amount",
+			"grand_total",
+			"base_grand_total",
+			"rounded_total",
+			"base_rounded_total",
+			"disable_rounded_total",
+		]
+		if doctype == "Sales Invoice":
+			fields.append("is_pos")
+
+		invoices_to_update = frappe.get_all(
+			doctype,
+			fields=fields,
+			filters={
+				"docstatus": 1,
+				"status": ("in", (
+					"Overdue",
+					"Overdue and Discounted",
+					"Partly Paid",
+					"Partly Paid and Discounted"
+				)),
+				"outstanding_amount": (">", 0),
+				"modified": (">", "2021-01-01")
+				# an assumption is being made that only invoices modified
+				# after 2021 got affected as incorrectly overdue.
+				# required for performance reasons.
+			}
+		)
+
+		invoices_to_update = {
+			invoice.name: invoice for invoice in invoices_to_update
+		}
+
+		payment_schedule_items = frappe.get_all(
+			"Payment Schedule",
+			fields=(
+				"due_date",
+				"payment_amount",
+				"base_payment_amount",
+				"parent"
+			),
+			filters={"parent": ("in", invoices_to_update)}
+		)
+
+		for item in payment_schedule_items:
+			invoices_to_update[item.parent].setdefault(
+				"payment_schedule", []
+			).append(item)
+
+		status_map = {}
+
+		for invoice in invoices_to_update.values():
+			invoice.doctype = doctype
+			doc = frappe.get_doc(invoice)
+			correct_status = get_correct_status(doc)
+			if not correct_status or doc.status == correct_status:
+				continue
+
+			status_map.setdefault(correct_status, []).append(doc.name)
+
+		for status, docs in status_map.items():
+			frappe.db.set_value(
+				doctype, {"name": ("in", docs)},
+				"status",
+				status,
+				update_modified=False
+			)
+
+
+
+def get_correct_status(doc):
+	outstanding_amount = flt(
+		doc.outstanding_amount, doc.precision("outstanding_amount")
+	)
+	total = get_total_in_party_account_currency(doc)
+
+	status = ""
+	if is_overdue(doc, total):
+		status = "Overdue"
+	elif 0 < outstanding_amount < total:
+		status = "Partly Paid"
+	elif outstanding_amount > 0 and getdate(doc.due_date) >= TODAY:
+		status = "Unpaid"
+
+	if not status:
+		return
+
+	if doc.status.endswith(" and Discounted"):
+		status += " and Discounted"
+
+	return status
diff --git a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
index fa8a864..3af7dac 100644
--- a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
+++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py
@@ -17,7 +17,7 @@
 		where
 			ref_exchange_rate = 1
 			and docstatus = 1
-			and ifnull(exchange_gain_loss, '') != ''
+			and ifnull(exchange_gain_loss, 0) != 0
 		group by
 			parent
 	""", as_dict=1)
@@ -30,7 +30,7 @@
 		where
 			ref_exchange_rate = 1
 			and docstatus = 1
-			and ifnull(exchange_gain_loss, '') != ''
+			and ifnull(exchange_gain_loss, 0) != 0
 		group by
 			parent
 	""", as_dict=1)
@@ -38,12 +38,24 @@
 	if purchase_invoices + sales_invoices:
 		frappe.log_error(json.dumps(purchase_invoices + sales_invoices, indent=2), title="Patch Log")
 
+	acc_frozen_upto = frappe.db.get_value('Accounts Settings', None, 'acc_frozen_upto')
+	if acc_frozen_upto:
+		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
+
 	for invoice in purchase_invoices + sales_invoices:
-		doc = frappe.get_doc(invoice.type, invoice.name)
-		doc.docstatus = 2
-		doc.make_gl_entries()
-		for advance in doc.advances:
-			if advance.ref_exchange_rate == 1:
-				advance.db_set('exchange_gain_loss', 0, False)
-		doc.docstatus = 1
-		doc.make_gl_entries()
\ No newline at end of file
+		try:
+			doc = frappe.get_doc(invoice.type, invoice.name)
+			doc.docstatus = 2
+			doc.make_gl_entries()
+			for advance in doc.advances:
+				if advance.ref_exchange_rate == 1:
+					advance.db_set('exchange_gain_loss', 0, False)
+			doc.docstatus = 1
+			doc.make_gl_entries()
+			frappe.db.commit()
+		except Exception:
+			frappe.db.rollback()
+			print(f'Failed to correct gl entries of {invoice.name}')
+
+	if acc_frozen_upto:
+		frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', acc_frozen_upto)
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/requeue_failed_reposts.py b/erpnext/patches/v13_0/requeue_failed_reposts.py
new file mode 100644
index 0000000..213cb9e
--- /dev/null
+++ b/erpnext/patches/v13_0/requeue_failed_reposts.py
@@ -0,0 +1,13 @@
+import frappe
+from frappe.utils import cstr
+
+
+def execute():
+
+	reposts = frappe.get_all("Repost Item Valuation",
+		{"status": "Failed", "modified": [">", "2021-10-05"] },
+		["name", "modified", "error_log"])
+
+	for repost in reposts:
+		if "check_freezing_date" in cstr(repost.error_log):
+			frappe.db.set_value("Repost Item Valuation", repost.name, "status", "Queued")
diff --git a/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
similarity index 81%
rename from erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
rename to erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
index 69bfaaa..902707b 100644
--- a/erpnext/patches/v12_0/update_vehicle_no_reqd_condition.py
+++ b/erpnext/patches/v13_0/update_vehicle_no_reqd_condition.py
@@ -2,7 +2,7 @@
 
 
 def execute():
-	frappe.reload_doc('custom', 'doctype', 'custom_field')
+	frappe.reload_doc('custom', 'doctype', 'custom_field', force=True)
 	company = frappe.get_all('Company', filters = {'country': 'India'})
 	if not company:
 		return
diff --git a/erpnext/patches/v14_0/delete_healthcare_doctypes.py b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
new file mode 100644
index 0000000..28fc01b
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
@@ -0,0 +1,49 @@
+import frappe
+
+
+def execute():
+	if "healthcare" in frappe.get_installed_apps():
+		return
+
+	frappe.delete_doc("Workspace", "Healthcare", ignore_missing=True, force=True)
+
+	pages = frappe.get_all("Page", {"module": "healthcare"}, pluck='name')
+	for page in pages:
+		frappe.delete_doc("Page", page, ignore_missing=True, force=True)
+
+	reports = frappe.get_all("Report", {"module": "healthcare", "is_standard": "Yes"}, pluck='name')
+	for report in reports:
+		frappe.delete_doc("Report", report, ignore_missing=True, force=True)
+
+	print_formats = frappe.get_all("Print Format", {"module": "healthcare", "standard": "Yes"}, pluck='name')
+	for print_format in print_formats:
+		frappe.delete_doc("Print Format", print_format, ignore_missing=True, force=True)
+
+	frappe.reload_doc("website", "doctype", "website_settings")
+	forms = frappe.get_all("Web Form", {"module": "healthcare", "is_standard": 1}, pluck='name')
+	for form in forms:
+		frappe.delete_doc("Web Form", form, ignore_missing=True, force=True)
+
+	dashboards = frappe.get_all("Dashboard", {"module": "healthcare", "is_standard": 1}, pluck='name')
+	for dashboard in dashboards:
+		frappe.delete_doc("Dashboard", dashboard, ignore_missing=True, force=True)
+
+	dashboards = frappe.get_all("Dashboard Chart", {"module": "healthcare", "is_standard": 1}, pluck='name')
+	for dashboard in dashboards:
+		frappe.delete_doc("Dashboard Chart", dashboard, ignore_missing=True, force=True)
+
+	frappe.reload_doc("desk", "doctype", "number_card")
+	cards = frappe.get_all("Number Card", {"module": "healthcare", "is_standard": 1}, pluck='name')
+	for card in cards:
+		frappe.delete_doc("Number Card", card, ignore_missing=True, force=True)
+
+	titles = ['Lab Test', 'Prescription', 'Patient Appointment']
+	items = frappe.get_all('Portal Menu Item', filters=[['title', 'in', titles]], pluck='name')
+	for item in items:
+		frappe.delete_doc("Portal Menu Item", item, ignore_missing=True, force=True)
+
+	doctypes = frappe.get_all("DocType", {"module": "healthcare", "custom": 0}, pluck='name')
+	for doctype in doctypes:
+		frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+	frappe.delete_doc("Module Def", "Healthcare", ignore_missing=True, force=True)
diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py
index 7c0a8ea..b6377f4 100644
--- a/erpnext/payroll/doctype/additional_salary/additional_salary.py
+++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py
@@ -125,27 +125,28 @@
 		no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
 		return amount_per_day * no_of_days
 
+@frappe.whitelist()
 def get_additional_salaries(employee, start_date, end_date, component_type):
-	additional_salary_list = frappe.db.sql("""
-		select name, salary_component as component, type, amount,
-		overwrite_salary_structure_amount as overwrite,
-		deduct_full_tax_on_selected_payroll_date
-		from `tabAdditional Salary`
-		where employee=%(employee)s
-			and docstatus = 1
-			and (
-					payroll_date between %(from_date)s and %(to_date)s
-				or
-					from_date <= %(to_date)s and to_date >= %(to_date)s
-				)
-		and type = %(component_type)s
-		order by salary_component, overwrite ASC
-	""", {
-		'employee': employee,
-		'from_date': start_date,
-		'to_date': end_date,
-		'component_type': "Earning" if component_type == "earnings" else "Deduction"
-	}, as_dict=1)
+	comp_type = 'Earning' if component_type == 'earnings' else 'Deduction'
+
+	additional_sal = frappe.qb.DocType('Additional Salary')
+	component_field = additional_sal.salary_component.as_('component')
+	overwrite_field = additional_sal.overwrite_salary_structure_amount.as_('overwrite')
+
+	additional_salary_list = frappe.qb.from_(
+		additional_sal
+	).select(
+		additional_sal.name, component_field, additional_sal.type,
+		additional_sal.amount, additional_sal.is_recurring, overwrite_field,
+		additional_sal.deduct_full_tax_on_selected_payroll_date
+	).where(
+		(additional_sal.employee == employee)
+		& (additional_sal.docstatus == 1)
+		& (additional_sal.type == comp_type)
+	).where(
+		additional_sal.payroll_date[start_date: end_date]
+		| ((additional_sal.from_date <= end_date) & (additional_sal.to_date >= end_date))
+	).run(as_dict=True)
 
 	additional_salaries = []
 	components_to_overwrite = []
diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json
index 393f647..665f0a8 100644
--- a/erpnext/payroll/doctype/salary_detail/salary_detail.json
+++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json
@@ -12,6 +12,7 @@
   "year_to_date",
   "section_break_5",
   "additional_salary",
+  "is_recurring_additional_salary",
   "statistical_component",
   "depends_on_payment_days",
   "exempted_from_income_tax",
@@ -235,11 +236,19 @@
    "label": "Year To Date",
    "options": "currency",
    "read_only": 1
-  }
+  },
+  {
+    "default": "0",
+    "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.additional_salary",
+    "fieldname": "is_recurring_additional_salary",
+    "fieldtype": "Check",
+    "label": "Is Recurring Additional Salary",
+    "read_only": 1
+   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-01-14 13:39:15.847158",
+ "modified": "2021-08-30 13:39:15.847158",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Detail",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json
index 1974403..7a80e69 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.json
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json
@@ -329,7 +329,7 @@
   {
    "fieldname": "earning_deduction",
    "fieldtype": "Section Break",
-   "label": "Earning & Deduction",
+   "label": "Earnings & Deductions",
    "oldfieldtype": "Section Break"
   },
   {
@@ -380,7 +380,7 @@
    "depends_on": "total_loan_repayment",
    "fieldname": "loan_repayment",
    "fieldtype": "Section Break",
-   "label": "Loan repayment"
+   "label": "Loan Repayment"
   },
   {
    "fieldname": "loans",
@@ -425,7 +425,7 @@
   {
    "fieldname": "net_pay_info",
    "fieldtype": "Section Break",
-   "label": "net pay info"
+   "label": "Net Pay Info"
   },
   {
    "fieldname": "net_pay",
@@ -647,7 +647,7 @@
  "idx": 9,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-01 10:35:52.374549",
+ "modified": "2021-10-08 11:47:47.098248",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Salary Slip",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index d113e7e..bee96b6 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -172,7 +172,6 @@
 						and employee = %s and name != %s {0}""".format(cond),
 						(self.start_date, self.end_date, self.employee, self.name))
 			if ret_exist:
-				self.employee = ''
 				frappe.throw(_("Salary Slip of employee {0} already created for this period").format(self.employee))
 		else:
 			for data in self.timesheets:
@@ -630,7 +629,8 @@
 				get_salary_component_data(additional_salary.component),
 				additional_salary.amount,
 				component_type,
-				additional_salary
+				additional_salary,
+				is_recurring = additional_salary.is_recurring
 			)
 
 	def add_tax_components(self, payroll_period):
@@ -651,7 +651,7 @@
 			tax_row = get_salary_component_data(d)
 			self.update_component_row(tax_row, tax_amount, "deductions")
 
-	def update_component_row(self, component_data, amount, component_type, additional_salary=None):
+	def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0):
 		component_row = None
 		for d in self.get(component_type):
 			if d.salary_component != component_data.salary_component:
@@ -698,6 +698,8 @@
 			else:
 				component_row.default_amount = 0
 				component_row.additional_amount = amount
+
+			component_row.is_recurring_additional_salary = is_recurring
 			component_row.additional_salary = additional_salary.name
 			component_row.deduct_full_tax_on_selected_payroll_date = \
 				additional_salary.deduct_full_tax_on_selected_payroll_date
@@ -894,25 +896,33 @@
 					amount, additional_amount = earning.default_amount, earning.additional_amount
 
 			if earning.is_tax_applicable:
-				if additional_amount:
-					taxable_earnings += (amount - additional_amount)
-					additional_income += additional_amount
-					if earning.deduct_full_tax_on_selected_payroll_date:
-						additional_income_with_full_tax += additional_amount
-					continue
-
 				if earning.is_flexible_benefit:
 					flexi_benefits += amount
 				else:
-					taxable_earnings += amount
+					taxable_earnings += (amount - additional_amount)
+					additional_income += additional_amount
+
+					# Get additional amount based on future recurring additional salary
+					if additional_amount and earning.is_recurring_additional_salary:
+						additional_income += self.get_future_recurring_additional_amount(earning.additional_salary,
+							earning.additional_amount) # Used earning.additional_amount to consider the amount for the full month
+
+					if earning.deduct_full_tax_on_selected_payroll_date:
+						additional_income_with_full_tax += additional_amount
 
 		if allow_tax_exemption:
 			for ded in self.deductions:
 				if ded.exempted_from_income_tax:
-					amount = ded.amount
+					amount, additional_amount = ded.amount, ded.additional_amount
 					if based_on_payment_days:
-						amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)[0]
-					taxable_earnings -= flt(amount)
+						amount, additional_amount = self.get_amount_based_on_payment_days(ded, joining_date, relieving_date)
+
+					taxable_earnings -= flt(amount - additional_amount)
+					additional_income -= additional_amount
+
+					if additional_amount and ded.is_recurring_additional_salary:
+						additional_income -= self.get_future_recurring_additional_amount(ded.additional_salary,
+							ded.additional_amount) # Used ded.additional_amount to consider the amount for the full month
 
 		return frappe._dict({
 			"taxable_earnings": taxable_earnings,
@@ -921,11 +931,21 @@
 			"flexi_benefits": flexi_benefits
 		})
 
+	def get_future_recurring_additional_amount(self, additional_salary, monthly_additional_amount):
+		future_recurring_additional_amount = 0
+		to_date = frappe.db.get_value("Additional Salary", additional_salary, 'to_date')
+		# future month count excluding current
+		future_recurring_period = (getdate(to_date).month - getdate(self.start_date).month)
+		if future_recurring_period > 0:
+			future_recurring_additional_amount = monthly_additional_amount * future_recurring_period # Used earning.additional_amount to consider the amount for the full month
+		return future_recurring_additional_amount
+
 	def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
 		amount, additional_amount = row.amount, row.additional_amount
 		if (self.salary_structure and
-			cint(row.depends_on_payment_days) and cint(self.total_working_days) and
-			(not self.salary_slip_based_on_timesheet or
+			cint(row.depends_on_payment_days) and cint(self.total_working_days)
+			and not (row.additional_salary and row.default_amount) # to identify overwritten additional salary
+			and (not self.salary_slip_based_on_timesheet or
 				getdate(self.start_date) < joining_date or
 				(relieving_date and getdate(self.end_date) > relieving_date)
 			)):
@@ -1244,7 +1264,7 @@
 
 		salary_slip_sum = frappe.get_list('Salary Slip',
 			fields = ['sum(net_pay) as net_sum', 'sum(gross_pay) as gross_sum'],
-			filters = {'employee_name' : self.employee_name,
+			filters = {'employee' : self.employee,
 				'start_date' : ['>=', period_start_date],
 				'end_date' : ['<', period_end_date],
 				'name': ['!=', self.name],
@@ -1264,7 +1284,7 @@
 		first_day_of_the_month = get_first_day(self.start_date)
 		salary_slip_sum = frappe.get_list('Salary Slip',
 			fields = ['sum(net_pay) as sum'],
-			filters = {'employee_name' : self.employee_name,
+			filters = {'employee' : self.employee,
 				'start_date' : ['>=', first_day_of_the_month],
 				'end_date' : ['<', self.start_date],
 				'name': ['!=', self.name],
@@ -1288,13 +1308,13 @@
 					INNER JOIN `tabSalary Slip` as salary_slip
 					ON detail.parent = salary_slip.name
 					WHERE
-						salary_slip.employee_name = %(employee_name)s
+						salary_slip.employee = %(employee)s
 						AND detail.salary_component = %(component)s
 						AND salary_slip.start_date >= %(period_start_date)s
 						AND salary_slip.end_date < %(period_end_date)s
 						AND salary_slip.name != %(docname)s
 						AND salary_slip.docstatus = 1""",
-						{'employee_name': self.employee_name, 'component': component.salary_component, 'period_start_date': period_start_date,
+						{'employee': self.employee, 'component': component.salary_component, 'period_start_date': period_start_date,
 							'period_end_date': period_end_date, 'docname': self.name}
 				)
 
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 9ed6686..c4b6a38 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -141,7 +141,6 @@
 			create_salary_structure_assignment,
 		)
 
-		no_of_days = self.get_no_of_days()
 		# Payroll based on attendance
 		frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
 
@@ -168,9 +167,6 @@
 		ss = make_salary_slip_for_payment_days_dependency_test("test_payment_days_based_component@salary.com", salary_structure.name)
 		self.assertEqual(ss.absent_days, 1)
 
-		days_in_month = no_of_days[0]
-		no_of_holidays = no_of_days[1]
-
 		ss.reload()
 		payment_days_based_comp_amount = 0
 		for component in ss.earnings:
@@ -540,6 +536,61 @@
 		# undelete fixture data
 		frappe.db.rollback()
 
+	def test_tax_for_recurring_additional_salary(self):
+		frappe.db.sql("""delete from `tabPayroll Period`""")
+		frappe.db.sql("""delete from `tabSalary Component`""")
+
+		payroll_period = create_payroll_period()
+
+		create_tax_slab(payroll_period, allow_tax_exemption=True)
+
+		employee = make_employee("test_tax@salary.slip")
+		delete_docs = [
+			"Salary Slip",
+			"Additional Salary",
+			"Employee Tax Exemption Declaration",
+			"Employee Tax Exemption Proof Submission",
+			"Employee Benefit Claim",
+			"Salary Structure Assignment"
+		]
+		for doc in delete_docs:
+			frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee))
+
+		from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+		salary_structure = make_salary_structure("Stucture to test tax", "Monthly",
+			other_details={"max_benefits": 100000}, test_tax=True,
+			employee=employee, payroll_period=payroll_period)
+
+
+		create_salary_slips_for_payroll_period(employee, salary_structure.name,
+			payroll_period, deduct_random=False, num=3)
+
+		tax_paid = get_tax_paid_in_period(employee)
+
+		annual_tax = 23196.0
+		self.assertEqual(tax_paid, annual_tax)
+
+		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+
+		#------------------------------------
+		# Recurring additional salary
+		start_date = add_months(payroll_period.start_date, 3)
+		end_date = add_months(payroll_period.start_date, 5)
+		create_recurring_additional_salary(employee, "Performance Bonus", 20000, start_date, end_date)
+
+		frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
+
+		create_salary_slips_for_payroll_period(employee, salary_structure.name,
+			payroll_period, deduct_random=False, num=4)
+
+		tax_paid = get_tax_paid_in_period(employee)
+
+		annual_tax = 32315.0
+		self.assertEqual(tax_paid, annual_tax)
+
+		frappe.db.rollback()
+
 	def make_activity_for_employee(self):
 		activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
 		activity_type.billing_rate = 50
@@ -992,13 +1043,14 @@
 	return salary_structure_doc
 
 def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure):
-	employee = frappe.db.get_value("Employee", {
-			"user_id": employee
-		},
+	employee = frappe.db.get_value(
+		"Employee",
+		{"user_id": employee},
 		["name", "company", "employee_name"],
-		as_dict=True)
+		as_dict=True
+	)
 
-	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": employee})})
+	salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name})
 
 	if not salary_slip_name:
 		salary_slip = make_salary_slip(salary_structure, employee=employee.name)
@@ -1009,4 +1061,18 @@
 	else:
 		salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
 
-	return salary_slip
\ No newline at end of file
+	return salary_slip
+
+def create_recurring_additional_salary(employee, salary_component, amount, from_date, to_date, company=None):
+	frappe.get_doc({
+		"doctype": "Additional Salary",
+		"employee": employee,
+		"company": company or erpnext.get_default_company(),
+		"salary_component": salary_component,
+		"is_recurring": 1,
+		"from_date": from_date,
+		"to_date": to_date,
+		"amount": amount,
+		"type": "Earning",
+		"currency": erpnext.get_default_currency()
+	}).submit()
diff --git a/erpnext/payroll/workspace/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json
index b55bdc7..7246dae 100644
--- a/erpnext/payroll/workspace/payroll/payroll.json
+++ b/erpnext/payroll/workspace/payroll/payroll.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Outgoing Salary",
@@ -8,18 +7,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Payroll\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Structure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payroll Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Slip\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Income Tax Slab\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payroll\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Compensations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
  "creation": "2020-05-27 19:54:23.405607",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "money-coins-1",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Payroll",
  "links": [
   {
@@ -319,15 +312,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:01.335324",
+ "modified": "2021-08-05 12:16:01.335325",
  "modified_by": "Administrator",
  "module": "Payroll",
  "name": "Payroll",
- "onboarding": "Payroll",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 1655b76..f615f05 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -32,12 +32,12 @@
 		};
 	},
 
-	onload: function(frm){
+	onload: function(frm) {
 		if (frm.doc.__islocal && frm.doc.time_logs) {
 			calculate_time_and_amount(frm);
 		}
 
-		if (frm.is_new()) {
+		if (frm.is_new() && !frm.doc.employee) {
 			set_employee_and_company(frm);
 		}
 	},
@@ -283,7 +283,9 @@
 		calculate_time_and_amount(frm);
 	},
 
-	activity_type: function(frm, cdt, cdn) {
+	activity_type: function (frm, cdt, cdn) {
+		if (!frappe.get_doc(cdt, cdn).activity_type) return;
+
 		frappe.call({
 			method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
 			args: {
@@ -291,10 +293,10 @@
 				activity_type: frm.selected_doc.activity_type,
 				currency: frm.doc.currency
 			},
-			callback: function(r){
-				if(r.message){
-					frappe.model.set_value(cdt, cdn, 'billing_rate', r.message['billing_rate']);
-					frappe.model.set_value(cdt, cdn, 'costing_rate', r.message['costing_rate']);
+			callback: function (r) {
+				if (r.message) {
+					frappe.model.set_value(cdt, cdn, "billing_rate", r.message["billing_rate"]);
+					frappe.model.set_value(cdt, cdn, "costing_rate", r.message["costing_rate"]);
 					calculate_billing_costing_amount(frm, cdt, cdn);
 				}
 			}
diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json
index 065f1ed..1df2b08 100644
--- a/erpnext/projects/workspace/projects/projects.json
+++ b/erpnext/projects/workspace/projects/projects.json
@@ -1,5 +1,4 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Project Summary",
@@ -8,18 +7,12 @@
  ],
  "content": "[{\"type\": \"chart\", \"data\": {\"chart_name\": \"Open Projects\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Task\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Timesheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project Billing Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Projects\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Time Tracking\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
  "creation": "2020-03-02 15:46:04.874669",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "project",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Projects",
  "links": [
   {
@@ -201,15 +194,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:01.540145",
+ "modified": "2021-08-05 12:16:01.540147",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Projects",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 702064f..b5a6d8f 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -137,7 +137,9 @@
 		var me = this;
 
 		$.each(this.frm.doc["taxes"] || [], function(i, tax) {
-			tax.item_wise_tax_detail = {};
+			if (!tax.dont_recompute_tax) {
+				tax.item_wise_tax_detail = {};
+			}
 			var tax_fields = ["total", "tax_amount_after_discount_amount",
 				"tax_amount_for_current_item", "grand_total_for_current_item",
 				"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"];
@@ -421,7 +423,9 @@
 			current_tax_amount = tax_rate * item.qty;
 		}
 
-		this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
+		if (!tax.dont_recompute_tax) {
+			this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
+		}
 
 		return current_tax_amount;
 	}
@@ -589,7 +593,9 @@
 					delete tax[fieldname];
 				});
 
-				tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
+				if (!tax.dont_recompute_tax) {
+					tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
+				}
 			});
 		}
 	}
diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js
index d0c935f..b643cca 100644
--- a/erpnext/public/js/help_links.js
+++ b/erpnext/public/js/help_links.js
@@ -5,7 +5,7 @@
 frappe.help.help_links["Form/Rename Tool"] = [
 	{
 		label: "Bulk Rename",
-		url: docsUrl + "user/manual/en/setting-up/data/bulk-rename",
+		url: docsUrl + "user/manual/en/using-erpnext/articles/bulk-rename",
 	},
 ];
 
@@ -59,10 +59,23 @@
 	},
 ];
 
-frappe.help.help_links["data-import-tool"] = [
+frappe.help.help_links["Form/Data Import"] = [
 	{
 		label: "Importing and Exporting Data",
-		url: docsUrl + "user/manual/en/setting-up/data/data-import-tool",
+		url: docsUrl + "user/manual/en/setting-up/data/data-import",
+	},
+	{
+		label: "Overwriting Data from Data Import Tool",
+		url:
+			docsUrl +
+			"user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool",
+	},
+];
+
+frappe.help.help_links["List/Data Import"] = [
+	{
+		label: "Importing and Exporting Data",
+		url: docsUrl + "user/manual/en/setting-up/data/data-import",
 	},
 	{
 		label: "Overwriting Data from Data Import Tool",
@@ -101,14 +114,14 @@
 	},
 ];
 
-frappe.help.help_links["Form/Email Digest"] = [
+frappe.help.help_links["List/Print Heading"] = [
 	{
-		label: "Email Digest",
-		url: docsUrl + "user/manual/en/setting-up/email/email-digest",
+		label: "Print Heading",
+		url: docsUrl + "user/manual/en/setting-up/print/print-headings",
 	},
 ];
 
-frappe.help.help_links["List/Print Heading"] = [
+frappe.help.help_links["Form/Print Heading"] = [
 	{
 		label: "Print Heading",
 		url: docsUrl + "user/manual/en/setting-up/print/print-headings",
@@ -153,18 +166,25 @@
 frappe.help.help_links["List/Notification"] = [
 	{
 		label: "Notification",
-		url: docsUrl + "user/manual/en/setting-up/email/notifications",
+		url: docsUrl + "user/manual/en/setting-up/notifications",
 	},
 ];
 
 frappe.help.help_links["Form/Notification"] = [
 	{
 		label: "Notification",
-		url: docsUrl + "user/manual/en/setting-up/email/notifications",
+		url: docsUrl + "user/manual/en/setting-up/notifications",
 	},
 ];
 
-frappe.help.help_links["List/Email Digest"] = [
+frappe.help.help_links["Form/Email Digest"] = [
+	{
+		label: "Email Digest",
+		url: docsUrl + "user/manual/en/setting-up/email/email-digest",
+	},
+];
+
+frappe.help.help_links["Form/Email Digest"] = [
 	{
 		label: "Email Digest",
 		url: docsUrl + "user/manual/en/setting-up/email/email-digest",
@@ -174,7 +194,7 @@
 frappe.help.help_links["List/Auto Email Report"] = [
 	{
 		label: "Auto Email Reports",
-		url: docsUrl + "user/manual/en/setting-up/email/email-reports",
+		url: docsUrl + "user/manual/en/setting-up/email/auto-email-reports",
 	},
 ];
 
@@ -188,14 +208,7 @@
 frappe.help.help_links["print-format-builder"] = [
 	{
 		label: "Print Format Builder",
-		url: docsUrl + "user/manual/en/setting-up/print/print-settings",
-	},
-];
-
-frappe.help.help_links["List/Print Heading"] = [
-	{
-		label: "Print Heading",
-		url: docsUrl + "user/manual/en/setting-up/print/print-headings",
+		url: docsUrl + "user/manual/en/setting-up/print/print-format-builder",
 	},
 ];
 
@@ -300,7 +313,7 @@
 	},
 	{
 		label: "Recurring Sales Order",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 	{
 		label: "Applying Discount",
@@ -315,7 +328,7 @@
 	},
 	{
 		label: "Recurring Sales Order",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 	{
 		label: "Applying Discount",
@@ -344,14 +357,14 @@
 frappe.help.help_links["Form/Product Bundle"] = [
 	{
 		label: "Product Bundle",
-		url: docsUrl + "user/manual/en/selling/setup/product-bundle",
+		url: docsUrl + "user/manual/en/selling/product-bundle",
 	},
 ];
 
 frappe.help.help_links["Form/Selling Settings"] = [
 	{
 		label: "Selling Settings",
-		url: docsUrl + "user/manual/en/selling/setup/selling-settings",
+		url: docsUrl + "user/manual/en/selling/selling-settings",
 	},
 ];
 
@@ -397,7 +410,7 @@
 	},
 	{
 		label: "Recurring Purchase Order",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 ];
 
@@ -420,7 +433,7 @@
 	},
 	{
 		label: "Recurring Purchase Order",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 	{
 		label: "Subcontracting",
@@ -435,24 +448,17 @@
 	},
 ];
 
-frappe.help.help_links["List/POS Profile"] = [
-	{
-		label: "POS Profile",
-		url: docsUrl + "user/manual/en/setting-up/pos-setting",
-	},
-];
-
 frappe.help.help_links["List/Price List"] = [
 	{
 		label: "Price List",
-		url: docsUrl + "user/manual/en/setting-up/price-lists",
+		url: docsUrl + "user/manual/en/stock/price-lists",
 	},
 ];
 
 frappe.help.help_links["List/Authorization Rule"] = [
 	{
 		label: "Authorization Rule",
-		url: docsUrl + "user/manual/en/setting-up/authorization-rule",
+		url: docsUrl + "user/manual/en/customize-erpnext/authorization-rule",
 	},
 ];
 
@@ -468,27 +474,14 @@
 		label: "Stock Reconciliation",
 		url:
 			docsUrl +
-			"user/manual/en/setting-up/stock-reconciliation-for-non-serialized-item",
+			"user/manual/en/stock/stock-reconciliation",
 	},
 ];
 
 frappe.help.help_links["Tree/Territory"] = [
 	{
 		label: "Territory",
-		url: docsUrl + "user/manual/en/setting-up/territory",
-	},
-];
-
-frappe.help.help_links["Form/Dropbox Backup"] = [
-	{
-		label: "Dropbox Backup",
-		url: docsUrl + "user/manual/en/setting-up/third-party-backups",
-	},
-	{
-		label: "Setting Up Dropbox Backup",
-		url:
-			docsUrl +
-			"user/manual/en/setting-up/articles/setting-up-dropbox-backups",
+		url: docsUrl + "user/manual/en/selling/territory",
 	},
 ];
 
@@ -502,12 +495,6 @@
 		url: docsUrl + "user/manual/en/setting-up/company-setup",
 	},
 	{
-		label: "Managing Multiple Companies",
-		url:
-			docsUrl +
-			"user/manual/en/setting-up/articles/managing-multiple-companies",
-	},
-	{
 		label: "Delete All Related Transactions for a Company",
 		url:
 			docsUrl +
@@ -517,21 +504,6 @@
 
 //Accounts
 
-frappe.help.help_links["modules/Accounts"] = [
-	{
-		label: "Introduction to Accounts",
-		url: docsUrl + "user/manual/en/accounts/",
-	},
-	{
-		label: "Chart of Accounts",
-		url: docsUrl + "user/manual/en/accounts/chart-of-accounts.html",
-	},
-	{
-		label: "Multi Currency Accounting",
-		url: docsUrl + "user/manual/en/accounts/multi-currency-accounting",
-	},
-];
-
 frappe.help.help_links["Tree/Account"] = [
 	{
 		label: "Chart of Accounts",
@@ -552,7 +524,7 @@
 	},
 	{
 		label: "Accounts Opening Balance",
-		url: docsUrl + "user/manual/en/accounts/opening-accounts",
+		url: docsUrl + "user/manual/en/accounts/opening-balance",
 	},
 	{
 		label: "Sales Return",
@@ -560,7 +532,7 @@
 	},
 	{
 		label: "Recurring Sales Invoice",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 ];
 
@@ -571,7 +543,7 @@
 	},
 	{
 		label: "Accounts Opening Balance",
-		url: docsUrl + "user/manual/en/accounts/opening-accounts",
+		url: docsUrl + "user/manual/en/accounts/opening-balances",
 	},
 	{
 		label: "Sales Return",
@@ -579,21 +551,28 @@
 	},
 	{
 		label: "Recurring Sales Invoice",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 ];
 
-frappe.help.help_links["pos"] = [
+frappe.help.help_links["point-of-sale"] = [
 	{
 		label: "Point of Sale Invoice",
-		url: docsUrl + "user/manual/en/accounts/point-of-sale-pos-invoice",
+		url: docsUrl + "user/manual/en/accounts/point-of-sales",
 	},
 ];
 
 frappe.help.help_links["List/POS Profile"] = [
 	{
 		label: "Point of Sale Profile",
-		url: docsUrl + "user/manual/en/setting-up/pos-setting",
+		url: docsUrl + "user/manual/en/accounts/pos-profile",
+	},
+];
+
+frappe.help.help_links["Form/POS Profile"] = [
+	{
+		label: "POS Profile",
+		url: docsUrl + "user/manual/en/accounts/pos-profile",
 	},
 ];
 
@@ -604,11 +583,11 @@
 	},
 	{
 		label: "Accounts Opening Balance",
-		url: docsUrl + "user/manual/en/accounts/opening-accounts",
+		url: docsUrl + "user/manual/en/accounts/opening-balance",
 	},
 	{
 		label: "Recurring Purchase Invoice",
-		url: docsUrl + "user/manual/en/accounts/recurring-orders-and-invoices",
+		url: docsUrl + "user/manual/en/accounts/articles/recurring-orders-and-invoices",
 	},
 ];
 
@@ -623,7 +602,7 @@
 	},
 	{
 		label: "Accounts Opening Balance",
-		url: docsUrl + "user/manual/en/accounts/opening-accounts",
+		url: docsUrl + "user/manual/en/accounts/opening-balance",
 	},
 ];
 
@@ -644,7 +623,7 @@
 frappe.help.help_links["List/Asset"] = [
 	{
 		label: "Managing Fixed Assets",
-		url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+		url: docsUrl + "user/manual/en/asset",
 	},
 ];
 
@@ -659,6 +638,8 @@
 	{ label: "Budgeting", url: docsUrl + "user/manual/en/accounts/budgeting" },
 ];
 
+//Stock
+
 frappe.help.help_links["List/Item"] = [
 	{ label: "Item", url: docsUrl + "user/manual/en/stock/item" },
 	{
@@ -676,7 +657,7 @@
 	},
 	{
 		label: "Managing Fixed Assets",
-		url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+		url: docsUrl + "user/manual/en/asset",
 	},
 	{
 		label: "Item Codification",
@@ -711,7 +692,7 @@
 	},
 	{
 		label: "Managing Fixed Assets",
-		url: docsUrl + "user/manual/en/accounts/opening-balance/fixed_assets",
+		url: docsUrl + "user/manual/en/asset",
 	},
 	{
 		label: "Item Codification",
@@ -771,10 +752,6 @@
 		url:
 			docsUrl + "user/manual/en/stock/articles/track-items-using-barcode",
 	},
-	{
-		label: "Subcontracting",
-		url: docsUrl + "user/manual/en/manufacturing/subcontracting",
-	},
 ];
 
 frappe.help.help_links["List/Installation Note"] = [
@@ -784,21 +761,10 @@
 	},
 ];
 
-frappe.help.help_links["Tree"] = [
-	{
-		label: "Managing Tree Structure Masters",
-		url:
-			docsUrl +
-			"user/manual/en/setting-up/articles/managing-tree-structure-masters",
-	},
-];
-
 frappe.help.help_links["List/Budget"] = [
 	{ label: "Budgeting", url: docsUrl + "user/manual/en/accounts/budgeting" },
 ];
 
-//Stock
-
 frappe.help.help_links["List/Material Request"] = [
 	{
 		label: "Material Request",
@@ -861,6 +827,10 @@
 	{ label: "Serial No", url: docsUrl + "user/manual/en/stock/serial-no" },
 ];
 
+frappe.help.help_links["List/Batch"] = [
+	{ label: "Batch", url: docsUrl + "user/manual/en/stock/batch" },
+];
+
 frappe.help.help_links["Form/Batch"] = [
 	{ label: "Batch", url: docsUrl + "user/manual/en/stock/batch" },
 ];
@@ -868,35 +838,35 @@
 frappe.help.help_links["Form/Packing Slip"] = [
 	{
 		label: "Packing Slip",
-		url: docsUrl + "user/manual/en/stock/tools/packing-slip",
+		url: docsUrl + "user/manual/en/stock/packing-slip",
 	},
 ];
 
 frappe.help.help_links["Form/Quality Inspection"] = [
 	{
 		label: "Quality Inspection",
-		url: docsUrl + "user/manual/en/stock/tools/quality-inspection",
+		url: docsUrl + "user/manual/en/stock/quality-inspection",
 	},
 ];
 
 frappe.help.help_links["Form/Landed Cost Voucher"] = [
 	{
 		label: "Landed Cost Voucher",
-		url: docsUrl + "user/manual/en/stock/tools/landed-cost-voucher",
+		url: docsUrl + "user/manual/en/stock/landed-cost-voucher",
 	},
 ];
 
 frappe.help.help_links["Tree/Item Group"] = [
 	{
 		label: "Item Group",
-		url: docsUrl + "user/manual/en/stock/setup/item-group",
+		url: docsUrl + "user/manual/en/stock/item-group",
 	},
 ];
 
 frappe.help.help_links["Form/Item Attribute"] = [
 	{
 		label: "Item Attribute",
-		url: docsUrl + "user/manual/en/stock/setup/item-attribute",
+		url: docsUrl + "user/manual/en/stock/item-attribute",
 	},
 ];
 
@@ -911,7 +881,7 @@
 frappe.help.help_links["Form/Stock Reconciliation"] = [
 	{
 		label: "Opening Stock Entry",
-		url: docsUrl + "user/manual/en/stock/opening-stock",
+		url: docsUrl + "user/manual/en/stock/stock-reconciliation",
 	},
 ];
 
@@ -938,13 +908,13 @@
 ];
 
 frappe.help.help_links["Form/Campaign"] = [
-	{ label: "Campaign", url: docsUrl + "user/manual/en/CRM/setup/campaign" },
+	{ label: "Campaign", url: docsUrl + "user/manual/en/CRM/campaign" },
 ];
 
 frappe.help.help_links["Tree/Sales Person"] = [
 	{
 		label: "Sales Person",
-		url: docsUrl + "user/manual/en/CRM/setup/sales-person",
+		url: docsUrl + "user/manual/en/CRM/sales-person",
 	},
 ];
 
@@ -953,30 +923,13 @@
 		label: "Sales Person Target",
 		url:
 			docsUrl +
-			"user/manual/en/selling/setup/sales-person-target-allocation",
+			"user/manual/en/selling/sales-person-target-allocation",
 	},
-];
-
-//Support
-
-frappe.help.help_links["List/Feedback Trigger"] = [
 	{
-		label: "Feedback Trigger",
-		url: docsUrl + "user/manual/en/setting-up/feedback/setting-up-feedback",
-	},
-];
-
-frappe.help.help_links["List/Feedback Request"] = [
-	{
-		label: "Feedback Request",
-		url: docsUrl + "user/manual/en/setting-up/feedback/submit-feedback",
-	},
-];
-
-frappe.help.help_links["List/Feedback Request"] = [
-	{
-		label: "Feedback Request",
-		url: docsUrl + "user/manual/en/setting-up/feedback/submit-feedback",
+		label: "Sales Person in Transactions",
+		url:
+			docsUrl +
+			"user/manual/en/selling/articles/sales-persons-in-the-sales-transactions",
 	},
 ];
 
@@ -1019,7 +972,7 @@
 frappe.help.help_links["Form/BOM Update Tool"] = [
 	{
 		label: "BOM Update Tool",
-		url: docsUrl + "user/manual/en/manufacturing/tools/bom-update-tool",
+		url: docsUrl + "user/manual/en/manufacturing/bom-update-tool",
 	},
 ];
 
@@ -1036,7 +989,7 @@
 	},
 ];
 
-frappe.help.help_links["Form/Custom Field"] = [
+frappe.help.help_links["List/Custom Field"] = [
 	{
 		label: "Custom Field",
 		url: docsUrl + "user/manual/en/customize-erpnext/custom-field",
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
index 7b35819..831626a 100644
--- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
@@ -334,10 +334,12 @@
 
 			if (child_nodes) {
 				$.each(child_nodes, (_i, data) => {
-					this.add_node(node, data);
-					setTimeout(() => {
-						this.add_connector(node.id, data.id);
-					}, 250);
+					if (!$(`[id="${data.id}"]`).length) {
+						this.add_node(node, data);
+						setTimeout(() => {
+							this.add_connector(node.id, data.id);
+						}, 250);
+					}
 				});
 			}
 		}
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 7f39b99..0323a42 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -712,6 +712,7 @@
 			allow_child_item_selection: opts.allow_child_item_selection,
 			child_fieldname: opts.child_fielname,
 			child_columns: opts.child_columns,
+			size: opts.size,
 			action: function(selections, args) {
 				let values = selections;
 				if (values.length === 0) {
diff --git a/erpnext/quality_management/workspace/quality/quality.json b/erpnext/quality_management/workspace/quality/quality.json
index 4dc8129..ae28470 100644
--- a/erpnext/quality_management/workspace/quality/quality.json
+++ b/erpnext/quality_management/workspace/quality/quality.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Goal\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Procedure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Inspection\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Review\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Action\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Conformance\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goal and Procedure\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Feedback\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Meeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Review and Action\", \"col\": 4}}]",
  "creation": "2020-03-02 15:49:28.632014",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "quality",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Quality",
  "links": [
   {
@@ -149,15 +142,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:01.699912",
+ "modified": "2021-08-05 12:16:01.699913",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index 45a689e..d7dcbf4 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -31,3 +31,4 @@
 		"document_name": doc.name,
 		"data": data
 	}).insert(ignore_permissions=True)
+
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json
index 95b930c..fc579d4 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.json
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.json
@@ -6,8 +6,10 @@
  "engine": "InnoDB",
  "field_order": [
   "gst_summary",
-  "column_break_2",
+  "gst_tax_settings_section",
   "round_off_gst_values",
+  "column_break_4",
+  "hsn_wise_tax_breakup",
   "gstin_email_sent_on",
   "section_break_4",
   "gst_accounts",
@@ -17,37 +19,23 @@
   {
    "fieldname": "gst_summary",
    "fieldtype": "HTML",
-   "label": "GST Summary",
-   "show_days": 1,
-   "show_seconds": 1
-  },
-  {
-   "fieldname": "column_break_2",
-   "fieldtype": "Column Break",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "GST Summary"
   },
   {
    "fieldname": "gstin_email_sent_on",
    "fieldtype": "Date",
    "label": "GSTIN Email Sent On",
-   "read_only": 1,
-   "show_days": 1,
-   "show_seconds": 1
+   "read_only": 1
   },
   {
    "fieldname": "section_break_4",
-   "fieldtype": "Section Break",
-   "show_days": 1,
-   "show_seconds": 1
+   "fieldtype": "Section Break"
   },
   {
    "fieldname": "gst_accounts",
    "fieldtype": "Table",
    "label": "GST Accounts",
-   "options": "GST Account",
-   "show_days": 1,
-   "show_seconds": 1
+   "options": "GST Account"
   },
   {
    "default": "250000",
@@ -56,24 +44,35 @@
    "fieldtype": "Data",
    "in_list_view": 1,
    "label": "B2C Limit",
-   "reqd": 1,
-   "show_days": 1,
-   "show_seconds": 1
+   "reqd": 1
   },
   {
    "default": "0",
    "description": "Enabling this option will round off individual GST components in all the Invoices",
    "fieldname": "round_off_gst_values",
    "fieldtype": "Check",
-   "label": "Round Off GST Values",
-   "show_days": 1,
-   "show_seconds": 1
+   "label": "Round Off GST Values"
+  },
+  {
+   "default": "0",
+   "fieldname": "hsn_wise_tax_breakup",
+   "fieldtype": "Check",
+   "label": "Tax Breakup Table Based On HSN Code"
+  },
+  {
+   "fieldname": "gst_tax_settings_section",
+   "fieldtype": "Section Break",
+   "label": "GST Tax Settings"
+  },
+  {
+   "fieldname": "column_break_4",
+   "fieldtype": "Column Break"
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-01-28 17:19:47.969260",
+ "modified": "2021-10-11 18:10:14.242614",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "GST Settings",
@@ -83,4 +82,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-   }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/doctype/ksa_vat_purchase_account/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
new file mode 100644
index 0000000..89ba3e9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "creation": "2021-07-13 09:17:09.862163",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "title",
+  "item_tax_template",
+  "account"
+ ],
+ "fields": [
+  {
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Title",
+   "reqd": 1
+  },
+  {
+   "fieldname": "item_tax_template",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Tax Template",
+   "options": "Item Tax Template",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-04 06:42:38.205597",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Purchase Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
new file mode 100644
index 0000000..3920bc5
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_purchase_account/ksa_vat_purchase_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATPurchaseAccount(Document):
+	pass
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/doctype/ksa_vat_sales_account/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
new file mode 100644
index 0000000..72613f4
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Sales Account', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
new file mode 100644
index 0000000..df27478
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "creation": "2021-07-13 08:46:33.820968",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "title",
+  "item_tax_template",
+  "account"
+ ],
+ "fields": [
+  {
+   "fieldname": "account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Account",
+   "options": "Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Title",
+   "reqd": 1
+  },
+  {
+   "fieldname": "item_tax_template",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Tax Template",
+   "options": "Item Tax Template",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-08-04 06:42:00.081407",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Sales Account",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
new file mode 100644
index 0000000..7c2689f
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSalesAccount(Document):
+	pass
diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
new file mode 100644
index 0000000..1d6a6a7
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_sales_account/test_ksa_vat_sales_account.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSalesAccount(unittest.TestCase):
+	pass
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/doctype/ksa_vat_setting/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/doctype/ksa_vat_setting/__init__.py
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
new file mode 100644
index 0000000..00b62b9
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Havenir Solutions and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('KSA VAT Setting', {
+	onload: function () {
+		frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting');
+	}
+});
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
new file mode 100644
index 0000000..3361946
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.json
@@ -0,0 +1,49 @@
+{
+ "actions": [],
+ "autoname": "field:company",
+ "creation": "2021-07-13 08:49:01.100356",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "ksa_vat_sales_accounts",
+  "ksa_vat_purchase_accounts"
+ ],
+ "fields": [
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1,
+   "unique": 1
+  },
+  {
+   "fieldname": "ksa_vat_sales_accounts",
+   "fieldtype": "Table",
+   "label": "KSA VAT Sales Accounts",
+   "options": "KSA VAT Sales Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "ksa_vat_purchase_accounts",
+   "fieldtype": "Table",
+   "label": "KSA VAT Purchase Accounts",
+   "options": "KSA VAT Purchase Account",
+   "reqd": 1
+  }
+ ],
+ "links": [],
+ "modified": "2021-08-26 04:29:06.499378",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT Setting",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "company",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
new file mode 100644
index 0000000..bdae116
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class KSAVATSetting(Document):
+	pass
diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
new file mode 100644
index 0000000..269cbec
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js
@@ -0,0 +1,5 @@
+frappe.listview_settings['KSA VAT Setting'] = {
+	onload () {
+		frappe.breadcrumbs.add('Accounts');
+	}
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
new file mode 100644
index 0000000..7207901
--- /dev/null
+++ b/erpnext/regional/doctype/ksa_vat_setting/test_ksa_vat_setting.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Havenir Solutions and Contributors
+# See license.txt
+
+# import frappe
+import unittest
+
+
+class TestKSAVATSetting(unittest.TestCase):
+	pass
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index afb1b07..1cbb154 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -615,10 +615,16 @@
 		],
 		'Supplier': [
 			{
+				'fieldname': 'pan',
+				'label': 'PAN',
+				'fieldtype': 'Data',
+				'insert_after': 'supplier_type'
+			},
+			{
 				'fieldname': 'gst_transporter_id',
 				'label': 'GST Transporter ID',
 				'fieldtype': 'Data',
-				'insert_after': 'supplier_type',
+				'insert_after': 'pan',
 				'depends_on': 'eval:doc.is_transporter'
 			},
 			{
@@ -641,10 +647,16 @@
 		],
 		'Customer': [
 			{
+				'fieldname': 'pan',
+				'label': 'PAN',
+				'fieldtype': 'Data',
+				'insert_after': 'customer_type'
+			},
+			{
 				'fieldname': 'gst_category',
 				'label': 'GST Category',
 				'fieldtype': 'Select',
-				'insert_after': 'customer_type',
+				'insert_after': 'pan',
 				'options': 'Registered Regular\nRegistered Composition\nUnregistered\nSEZ\nOverseas\nConsumer\nDeemed Export\nUIN Holders',
 				'default': 'Unregistered'
 			},
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 9493614..1733220 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -62,7 +62,7 @@
 				.format(doc.gst_state_number), title=_("Invalid GSTIN"))
 
 def validate_pan_for_india(doc, method):
-	if doc.get('country') != 'India' or not doc.pan:
+	if doc.get('country') != 'India' or not doc.get('pan'):
 		return
 
 	if not PAN_NUMBER_FORMAT.match(doc.pan):
@@ -112,7 +112,11 @@
 		frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label))
 
 def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
-	return [_("Item"), _("Taxable Amount")] + tax_accounts
+	hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
+	if frappe.get_meta(item_doctype).has_field('gst_hsn_code') and hsn_wise_in_gst_settings:
+		return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
+	else:
+		return [_("Item"), _("Taxable Amount")] + tax_accounts
 
 def get_itemised_tax_breakup_data(doc, account_wise=False, hsn_wise=False):
 	itemised_tax = get_itemised_tax(doc.taxes, with_tax_account=account_wise)
@@ -122,14 +126,17 @@
 	if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
 		return itemised_tax, itemised_taxable_amount
 
-	if hsn_wise:
+	hsn_wise_in_gst_settings = frappe.db.get_single_value('GST Settings','hsn_wise_tax_breakup')
+
+	tax_breakup_hsn_wise = hsn_wise or hsn_wise_in_gst_settings
+	if tax_breakup_hsn_wise:
 		item_hsn_map = frappe._dict()
 		for d in doc.items:
 			item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
 
 	hsn_tax = {}
 	for item, taxes in itemised_tax.items():
-		item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
+		item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
 		hsn_tax.setdefault(item_or_hsn, frappe._dict())
 		for tax_desc, tax_detail in taxes.items():
 			key = tax_desc
@@ -142,7 +149,7 @@
 	# set taxable amount
 	hsn_taxable_amount = frappe._dict()
 	for item in itemised_taxable_amount:
-		item_or_hsn = item if not hsn_wise else item_hsn_map.get(item)
+		item_or_hsn = item if not tax_breakup_hsn_wise else item_hsn_map.get(item)
 		hsn_taxable_amount.setdefault(item_or_hsn, 0)
 		hsn_taxable_amount[item_or_hsn] += itemised_taxable_amount.get(item)
 
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 23924c5..7d401ba 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -172,13 +172,6 @@
 		self.invoices = frappe._dict()
 		conditions = self.get_conditions()
 
-		company_gstins = get_company_gstin_number(self.filters.get('company'), all_gstins=True)
-
-		if company_gstins:
-			self.filters.update({
-				'company_gstins': company_gstins
-			})
-
 		invoice_data = frappe.db.sql("""
 			select
 				{select_columns}
@@ -242,7 +235,7 @@
 		elif self.filters.get("type_of_business") ==  "EXPORT":
 			conditions += """ AND is_return !=1 and gst_category = 'Overseas' """
 
-		conditions += " AND IFNULL(billing_address_gstin, '') NOT IN %(company_gstins)s"
+		conditions += " AND IFNULL(billing_address_gstin, '') != company_gstin"
 
 		return conditions
 
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/report/ksa_vat/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/report/ksa_vat/__init__.py
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.js b/erpnext/regional/report/ksa_vat/ksa_vat.js
new file mode 100644
index 0000000..d46d260
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.js
@@ -0,0 +1,60 @@
+// Copyright (c) 2016, Havenir Solutions and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["KSA VAT"] = {
+	onload() {
+		frappe.breadcrumbs.add('Accounts');
+	},
+	"filters": [
+		{
+			"fieldname": "company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"reqd": 1,
+			"default": frappe.defaults.get_user_default("Company")
+		},
+		{
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"reqd": 1,
+			"default": frappe.datetime.get_today()
+		}
+	],
+	"formatter": function(value, row, column, data, default_formatter) {
+		if (data
+			&& (data.title=='VAT on Sales' || data.title=='VAT on Purchases')
+			&& data.title==value) {
+			value = $(`<span>${value}</span>`);
+			var $value = $(value).css("font-weight", "bold");
+			value = $value.wrap("<p></p>").parent().html();
+			return value
+		}else if (data.title=='Grand Total'){
+			if (data.title==value) {
+				value = $(`<span>${value}</span>`);
+				var $value = $(value).css("font-weight", "bold");
+				value = $value.wrap("<p></p>").parent().html();
+				return value
+			}else{
+				value = default_formatter(value, row, column, data);
+				value = $(`<span>${value}</span>`);
+				var $value = $(value).css("font-weight", "bold");
+				value = $value.wrap("<p></p>").parent().html();
+				console.log($value)
+				return value
+			}
+		}else{
+			value = default_formatter(value, row, column, data);
+			return value;
+		}
+	},
+};
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.json b/erpnext/regional/report/ksa_vat/ksa_vat.json
new file mode 100644
index 0000000..036e260
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.json
@@ -0,0 +1,32 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-07-13 08:54:38.000949",
+ "disable_prepared_report": 1,
+ "disabled": 1,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2021-08-26 04:14:37.202594",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "KSA VAT",
+ "owner": "Administrator",
+ "prepared_report": 1,
+ "ref_doctype": "GL Entry",
+ "report_name": "KSA VAT",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Accounts Manager"
+  },
+  {
+   "role": "Accounts User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py
new file mode 100644
index 0000000..a42ebc9
--- /dev/null
+++ b/erpnext/regional/report/ksa_vat/ksa_vat.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2013, Havenir Solutions and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import json
+
+import frappe
+from frappe import _
+from frappe.utils import get_url_to_list
+
+
+def execute(filters=None):
+	columns = columns = get_columns()
+	data = get_data(filters)
+	return columns, data
+
+def get_columns():
+	return [
+		{
+			"fieldname": "title",
+			"label": _("Title"),
+			"fieldtype": "Data",
+			"width": 300
+		},
+		{
+			"fieldname": "amount",
+			"label": _("Amount (SAR)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		},
+		{
+			"fieldname": "adjustment_amount",
+			"label": _("Adjustment (SAR)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		},
+		{
+			"fieldname": "vat_amount",
+			"label": _("VAT Amount (SAR)"),
+			"fieldtype": "Currency",
+			"width": 150,
+		}
+	]
+
+def get_data(filters):
+	data = []
+
+	# Validate if vat settings exist
+	company = filters.get('company')
+	if frappe.db.exists('KSA VAT Setting', company) is None:
+		url = get_url_to_list('KSA VAT Setting')
+		frappe.msgprint(_('Create <a href="{}">KSA VAT Setting</a> for this company').format(url))
+		return data
+
+	ksa_vat_setting = frappe.get_doc('KSA VAT Setting', company)
+
+	# Sales Heading
+	append_data(data, 'VAT on Sales', '', '', '')
+
+	grand_total_taxable_amount = 0
+	grand_total_taxable_adjustment_amount = 0
+	grand_total_tax = 0
+
+	for vat_setting in ksa_vat_setting.ksa_vat_sales_accounts:
+		total_taxable_amount, total_taxable_adjustment_amount, \
+			total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Sales Invoice')
+
+		# Adding results to data
+		append_data(data, vat_setting.title, total_taxable_amount,
+			total_taxable_adjustment_amount, total_tax)
+
+		grand_total_taxable_amount += total_taxable_amount
+		grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
+		grand_total_tax += total_tax
+
+	# Sales Grand Total
+	append_data(data, 'Grand Total', grand_total_taxable_amount,
+		grand_total_taxable_adjustment_amount, grand_total_tax)
+
+	# Blank Line
+	append_data(data, '', '', '', '')
+
+	# Purchase Heading
+	append_data(data, 'VAT on Purchases', '', '', '')
+
+	grand_total_taxable_amount = 0
+	grand_total_taxable_adjustment_amount = 0
+	grand_total_tax = 0
+
+	for vat_setting in ksa_vat_setting.ksa_vat_purchase_accounts:
+		total_taxable_amount, total_taxable_adjustment_amount, \
+			total_tax = get_tax_data_for_each_vat_setting(vat_setting, filters, 'Purchase Invoice')
+
+		# Adding results to data
+		append_data(data, vat_setting.title, total_taxable_amount,
+			total_taxable_adjustment_amount, total_tax)
+
+		grand_total_taxable_amount += total_taxable_amount
+		grand_total_taxable_adjustment_amount += total_taxable_adjustment_amount
+		grand_total_tax += total_tax
+
+	# Purchase Grand Total
+	append_data(data, 'Grand Total', grand_total_taxable_amount,
+		grand_total_taxable_adjustment_amount, grand_total_tax)
+
+	return data
+
+def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype):
+	'''
+	(KSA, {filters}, 'Sales Invoice') => 500, 153, 10 \n
+	calculates and returns \n
+	total_taxable_amount, total_taxable_adjustment_amount, total_tax'''
+	from_date = filters.get('from_date')
+	to_date = filters.get('to_date')
+
+	# Initiate variables
+	total_taxable_amount = 0
+	total_taxable_adjustment_amount = 0
+	total_tax = 0
+	# Fetch All Invoices
+	invoices = frappe.get_list(doctype,
+	filters ={
+		'docstatus': 1,
+		'posting_date': ['between', [from_date, to_date]]
+	}, fields =['name', 'is_return'])
+
+	for invoice in invoices:
+		invoice_items = frappe.get_list(f'{doctype} Item',
+		filters ={
+			'docstatus': 1,
+			'parent': invoice.name,
+			'item_tax_template': vat_setting.item_tax_template
+		}, fields =['item_code', 'net_amount'])
+
+		for item in invoice_items:
+			# Summing up total taxable amount
+			if invoice.is_return == 0:
+				total_taxable_amount += item.net_amount
+
+			if invoice.is_return == 1:
+				total_taxable_adjustment_amount += item.net_amount
+
+			# Summing up total tax
+			total_tax += get_tax_amount(item.item_code, vat_setting.account, doctype, invoice.name)
+
+	return total_taxable_amount, total_taxable_adjustment_amount, total_tax
+
+
+
+def append_data(data, title, amount, adjustment_amount, vat_amount):
+	"""Returns data with appended value."""
+	data.append({"title": _(title), "amount": amount, "adjustment_amount": adjustment_amount, "vat_amount": vat_amount})
+
+def get_tax_amount(item_code, account_head, doctype, parent):
+	if doctype == 'Sales Invoice':
+		tax_doctype = 'Sales Taxes and Charges'
+
+	elif doctype == 'Purchase Invoice':
+		tax_doctype = 'Purchase Taxes and Charges'
+
+	item_wise_tax_detail = frappe.get_value(tax_doctype, {
+		'docstatus': 1,
+		'parent': parent,
+		'account_head': account_head
+	}, 'item_wise_tax_detail')
+
+	tax_amount = 0
+	if item_wise_tax_detail and len(item_wise_tax_detail) > 0:
+		item_wise_tax_detail = json.loads(item_wise_tax_detail)
+		for key, value in item_wise_tax_detail.items():
+			if key == item_code:
+				tax_amount = value[1]
+				break
+
+	return tax_amount
diff --git a/erpnext/regional/saudi_arabia/setup.py b/erpnext/regional/saudi_arabia/setup.py
index 9b3677d..6113f48 100644
--- a/erpnext/regional/saudi_arabia/setup.py
+++ b/erpnext/regional/saudi_arabia/setup.py
@@ -2,10 +2,36 @@
 # License: GNU General Public License v3. See license.txt
 
 from __future__ import unicode_literals
-
-from erpnext.regional.united_arab_emirates.setup import make_custom_fields, add_print_formats
-
+import frappe
+from frappe.permissions import add_permission, update_permission_property
+from erpnext.regional.united_arab_emirates.setup import make_custom_fields as uae_custom_fields, add_print_formats
+from erpnext.regional.saudi_arabia.wizard.operations.setup_ksa_vat_setting import create_ksa_vat_setting
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
 
 def setup(company=None, patch=True):
-	make_custom_fields()
+	uae_custom_fields()
 	add_print_formats()
+	add_permissions()
+	create_ksa_vat_setting(company)
+	make_qrcode_field()
+
+def add_permissions():
+	"""Add Permissions for KSA VAT Setting."""
+	add_permission('KSA VAT Setting', 'All', 0)
+	for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
+		add_permission('KSA VAT Setting', role, 0)
+		update_permission_property('KSA VAT Setting', role, 0, 'write', 1)
+		update_permission_property('KSA VAT Setting', role, 0, 'create', 1)
+
+	"""Enable KSA VAT Report"""
+	frappe.db.set_value('Report', 'KSA VAT', 'disabled', 0)
+
+def make_qrcode_field():
+	"""Created QR code Image file"""
+	qr_code_field = dict(
+		fieldname='qr_code',
+		label='QR Code',
+		fieldtype='Attach Image',
+		read_only=1, no_copy=1, hidden=1)
+
+	create_custom_field('Sales Invoice', qr_code_field)
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
new file mode 100644
index 0000000..cc6c0af
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -0,0 +1,77 @@
+import io
+import os
+
+import frappe
+from pyqrcode import create as qr_create
+
+from erpnext import get_region
+
+
+def create_qr_code(doc, method):
+	"""Create QR Code after inserting Sales Inv
+	"""
+
+	region = get_region(doc.company)
+	if region not in ['Saudi Arabia']:
+		return
+
+	# if QR Code field not present, do nothing
+	if not hasattr(doc, 'qr_code'):
+		return
+
+	# Don't create QR Code if it already exists
+	qr_code = doc.get("qr_code")
+	if qr_code and frappe.db.exists({"doctype": "File", "file_url": qr_code}):
+		return
+
+	meta = frappe.get_meta('Sales Invoice')
+
+	for field in meta.get_image_fields():
+		if field.fieldname == 'qr_code':
+			# Creating public url to print format
+			default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=doc.doctype), "value")
+
+			# System Language
+			language = frappe.get_system_settings('language')
+
+			# creating qr code for the url
+			url = f"{ frappe.utils.get_url() }/{ doc.doctype }/{ doc.name }?format={ default_print_format or 'Standard' }&_lang={ language }&key={ doc.get_signature() }"
+			qr_image = io.BytesIO()
+			url = qr_create(url, error='L')
+			url.png(qr_image, scale=2, quiet_zone=1)
+
+			# making file
+			filename = f"QR-CODE-{doc.name}.png".replace(os.path.sep, "__")
+			_file = frappe.get_doc({
+				"doctype": "File",
+				"file_name": filename,
+				"is_private": 0,
+				"content": qr_image.getvalue(),
+				"attached_to_doctype": doc.get("doctype"),
+				"attached_to_name": doc.get("name"),
+				"attached_to_field": "qr_code"
+			})
+
+			_file.save()
+
+			# assigning to document
+			doc.db_set('qr_code', _file.file_url)
+			doc.notify_update()
+
+			break
+
+
+def delete_qr_code_file(doc, method):
+	"""Delete QR Code on deleted sales invoice"""
+
+	region = get_region(doc.company)
+	if region not in ['Saudi Arabia']:
+		return
+
+	if hasattr(doc, 'qr_code'):
+		if doc.get('qr_code'):
+			file_doc = frappe.get_list('File', {
+				'file_url': doc.get('qr_code')
+			})
+			if len(file_doc):
+				frappe.delete_doc('File', file_doc[0].name)
\ No newline at end of file
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/saudi_arabia/wizard/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/saudi_arabia/wizard/__init__.py
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/saudi_arabia/wizard/data/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/saudi_arabia/wizard/data/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
new file mode 100644
index 0000000..709d65b
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/data/ksa_vat_settings.json
@@ -0,0 +1,47 @@
+[
+    {
+        "type": "Sales Account",
+        "accounts": [
+            {
+                "title": "Standard rated Sales",
+                "item_tax_template": "KSA VAT 5%",
+                "account": "VAT 5%"
+            },
+            {
+                "title": "Zero rated domestic sales",
+                "item_tax_template": "KSA VAT Zero",
+                "account": "VAT Zero"
+            },
+            {
+                "title": "Exempted sales",
+                "item_tax_template": "KSA VAT Exempted",
+                "account": "VAT Zero"
+            }
+        ]
+    },
+    {
+        "type": "Purchase Account",
+        "accounts": [
+            {
+                "title": "Standard rated domestic purchases",
+                "item_tax_template": "KSA VAT 5%",
+                "account": "VAT 5%"
+            },
+            {
+                "title": "Imports subject to VAT paid at customs",
+                "item_tax_template": "KSA Excise 50%",
+                "account": "Excise 50%"
+            },
+            {
+                "title": "Zero rated purchases",
+                "item_tax_template": "KSA VAT Zero",
+                "account": "VAT Zero"
+            },
+            {
+                "title": "Exempted purchases",
+                "item_tax_template": "KSA VAT Exempted",
+                "account": "VAT Zero"
+            }
+        ]
+    }
+]
\ No newline at end of file
diff --git a/erpnext/stock/report/process_loss_report/__init__.py b/erpnext/regional/saudi_arabia/wizard/operations/__init__.py
similarity index 100%
copy from erpnext/stock/report/process_loss_report/__init__.py
copy to erpnext/regional/saudi_arabia/wizard/operations/__init__.py
diff --git a/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
new file mode 100644
index 0000000..3c89edd
--- /dev/null
+++ b/erpnext/regional/saudi_arabia/wizard/operations/setup_ksa_vat_setting.py
@@ -0,0 +1,46 @@
+import json
+import os
+
+import frappe
+
+from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
+
+
+def create_ksa_vat_setting(company):
+    """On creation of first company. Creates KSA VAT Setting"""
+
+    company = frappe.get_doc('Company', company)
+    setup_taxes_and_charges(company.name, company.country)
+
+    file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'ksa_vat_settings.json')
+    with open(file_path, 'r') as json_file:
+        account_data = json.load(json_file)
+
+    # Creating KSA VAT Setting
+    ksa_vat_setting = frappe.get_doc({
+        'doctype': 'KSA VAT Setting',
+        'company': company.name
+    })
+
+    for data in account_data:
+        if data['type'] == 'Sales Account':
+            for row in data['accounts']:
+                item_tax_template = row['item_tax_template']
+                account = row['account']
+                ksa_vat_setting.append('ksa_vat_sales_accounts', {
+                    'title': row['title'],
+                    'item_tax_template': f'{item_tax_template} - {company.abbr}',
+                    'account': f'{account} - {company.abbr}'
+                })
+
+        elif data['type'] == 'Purchase Account':
+            for row in data['accounts']:
+                item_tax_template = row['item_tax_template']
+                account = row['account']
+                ksa_vat_setting.append('ksa_vat_purchase_accounts', {
+                    'title': row['title'],
+                    'item_tax_template': f'{item_tax_template} - {company.abbr}',
+                    'account': f'{account} - {company.abbr}'
+                })
+
+    ksa_vat_setting.save()
diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py
index 9c183af..cf78f92 100644
--- a/erpnext/regional/united_states/setup.py
+++ b/erpnext/regional/united_states/setup.py
@@ -14,30 +14,9 @@
 		setup_company_independent_fixtures(patch=patch)
 
 def setup_company_independent_fixtures(company=None, patch=True):
-	add_product_tax_categories()
 	make_custom_fields()
-	add_permissions()
-	frappe.enqueue('erpnext.regional.united_states.setup.add_product_tax_categories', now=False)
 	add_print_formats()
 
-# Product Tax categories imported from taxjar api
-def add_product_tax_categories():
-	with open(os.path.join(os.path.dirname(__file__), 'product_tax_category_data.json'), 'r') as f:
-		tax_categories = json.loads(f.read())
-	create_tax_categories(tax_categories['categories'])
-
-def create_tax_categories(data):
-	for d in data:
-		tax_category = frappe.new_doc('Product Tax Category')
-		tax_category.description = d.get("description")
-		tax_category.product_tax_code = d.get("product_tax_code")
-		tax_category.category_name = d.get("name")
-		try:
-			tax_category.db_insert()
-		except frappe.DuplicateEntryError:
-			pass
-
-
 def make_custom_fields(update=True):
 	custom_fields = {
 		'Supplier': [
@@ -59,29 +38,10 @@
 		'Quotation': [
 			dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges',
 				label='Is customer exempted from sales tax?')
-		],
-		'Sales Invoice Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='description', options='Product Tax Category',
-				label='Product Tax Category', fetch_from='item_code.product_tax_category'),
-			dict(fieldname='tax_collectable', fieldtype='Currency', insert_after='net_amount',
-				label='Tax Collectable', read_only=1),
-			dict(fieldname='taxable_amount', fieldtype='Currency', insert_after='tax_collectable',
-				label='Taxable Amount', read_only=1)
-		],
-		'Item': [
-			dict(fieldname='product_tax_category', fieldtype='Link', insert_after='item_group', options='Product Tax Category',
-				label='Product Tax Category')
 		]
 	}
 	create_custom_fields(custom_fields, update=update)
 
-def add_permissions():
-	doctype = "Product Tax Category"
-	for role in ('Accounts Manager', 'Accounts User', 'System Manager','Item Manager', 'Stock Manager'):
-		add_permission(doctype, role, 0)
-		update_permission_property(doctype, role, 0, 'write', 1)
-		update_permission_property(doctype, role, 0, 'create', 1)
-
 def add_print_formats():
 	frappe.reload_doc("regional", "print_format", "irs_1099_form")
 	frappe.db.set_value("Print Format", "IRS 1099 Form", "disabled", 0)
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index e811435..ae40630 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -16,7 +16,6 @@
   "customer_name",
   "gender",
   "customer_type",
-  "pan",
   "tax_withholding_category",
   "default_bank_account",
   "lead_name",
@@ -487,11 +486,6 @@
    "label": "Allow Sales Invoice Creation Without Delivery Note"
   },
   {
-   "fieldname": "pan",
-   "fieldtype": "Data",
-   "label": "PAN"
-  },
-  {
    "fieldname": "tax_withholding_category",
    "fieldtype": "Link",
    "label": "Tax Withholding Category",
@@ -517,11 +511,12 @@
    "link_fieldname": "party"
   }
  ],
- "modified": "2021-09-06 17:38:54.196663",
+ "modified": "2021-10-20 22:07:52.485809",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Customer",
  "name_case": "Title Case",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 79e40f6..d7f1f03 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -110,7 +110,7 @@
 		if self.order_type == 'Sales' and not self.skip_delivery_note:
 			delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date]
 			max_delivery_date = max(delivery_date_list) if delivery_date_list else None
-			if not self.delivery_date:
+			if (max_delivery_date and not self.delivery_date) or (max_delivery_date and getdate(self.delivery_date) != getdate(max_delivery_date)):
 				self.delivery_date = max_delivery_date
 			if self.delivery_date:
 				for d in self.get("items"):
@@ -119,8 +119,6 @@
 					if getdate(self.transaction_date) > getdate(d.delivery_date):
 						frappe.msgprint(_("Expected Delivery Date should be after Sales Order Date"),
 							indicator='orange', title=_('Warning'))
-				if getdate(self.delivery_date) != getdate(max_delivery_date):
-					self.delivery_date = max_delivery_date
 			else:
 				frappe.throw(_("Please enter Delivery Date"))
 
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index ca69603..e241661 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1454,7 +1454,6 @@
 
 	frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True)
 	frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True)
-	frappe.db.commit()
 	frappe.cache().hdel('roles', frappe.session.user)
 
 	workflow = frappe.get_doc({
diff --git a/erpnext/selling/workspace/retail/retail.json b/erpnext/selling/workspace/retail/retail.json
index 9d2e6ca..a851ace 100644
--- a/erpnext/selling/workspace/retail/retail.json
+++ b/erpnext/selling/workspace/retail/retail.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Point Of Sale\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings & Configurations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loyalty Program\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening & Closing\", \"col\": 4}}]",
  "creation": "2020-03-02 17:18:32.505616",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "retail",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Retail",
  "links": [
   {
@@ -108,15 +101,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:01.840988",
+ "modified": "2021-08-05 12:16:01.840989",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Retail",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "Retail",
  "roles": [],
diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json
index 345187f..db2e6ba 100644
--- a/erpnext/selling/workspace/selling/selling.json
+++ b/erpnext/selling/workspace/selling/selling.json
@@ -1,26 +1,18 @@
 {
- "category": "",
  "charts": [
   {
    "chart_name": "Sales Order Trends",
    "label": "Sales Order Trends"
   }
  ],
- "charts_label": "Selling ",
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Selling\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Sales Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Selling\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]",
  "creation": "2020-01-28 11:49:12.092882",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "sell",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Selling",
  "links": [
   {
@@ -570,15 +562,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:01.990702",
+ "modified": "2021-08-05 12:16:01.990703",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling",
- "onboarding": "Selling",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
@@ -619,6 +608,5 @@
    "type": "Dashboard"
   }
  ],
- "shortcuts_label": "Quick Access",
  "title": "Selling"
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 8403193..95ca386 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -46,43 +46,6 @@
 		});
 	},
 
-	change_abbreviation(frm) {
-		var dialog = new frappe.ui.Dialog({
-			title: "Replace Abbr",
-			fields: [
-				{"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr",
-					"reqd": 1 },
-				{"fieldtype": "Button", "label": "Update", "fieldname": "update"},
-			]
-		});
-
-		dialog.fields_dict.update.$input.click(function() {
-			var args = dialog.get_values();
-			if (!args) return;
-			frappe.show_alert(__("Update in progress. It might take a while."));
-			return frappe.call({
-				method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr",
-				args: {
-					"company": frm.doc.name,
-					"old": frm.doc.abbr,
-					"new": args.new_abbr
-				},
-				callback: function(r) {
-					if (r.exc) {
-						frappe.msgprint(__("There were errors."));
-						return;
-					} else {
-						frm.set_value("abbr", args.new_abbr);
-					}
-					dialog.hide();
-					frm.refresh();
-				},
-				btn: this
-			});
-		});
-		dialog.show();
-	},
-
 	company_name: function(frm) {
 		if(frm.doc.__islocal) {
 			// add missing " " arg in split method
@@ -164,10 +127,6 @@
 					}, __('Manage'));
 				}
 			}
-
-			frm.add_custom_button(__('Change Abbreviation'), () => {
-				frm.trigger('change_abbreviation');
-			}, __('Manage'));
 		}
 
 		erpnext.company.set_chart_of_accounts_options(frm.doc);
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 58cb52c..63d96bf 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -125,7 +125,8 @@
    "label": "Abbr",
    "oldfieldname": "abbr",
    "oldfieldtype": "Data",
-   "reqd": 1
+   "reqd": 1,
+   "set_only_once": 1
   },
   {
    "bold": 1,
@@ -747,10 +748,11 @@
  "image_field": "company_logo",
  "is_tree": 1,
  "links": [],
- "modified": "2021-07-12 11:27:06.353860",
+ "modified": "2021-10-04 12:09:25.833133",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Company",
+ "naming_rule": "By fieldname",
  "nsm_parent_field": "parent_company",
  "owner": "Administrator",
  "permissions": [
@@ -808,4 +810,4 @@
  "sort_field": "modified",
  "sort_order": "ASC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 87d67a5..0b1b4a1 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -399,44 +399,6 @@
 		if not frappe.db.get_value('GL Entry', {'company': self.name}):
 			frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name)
 
-@frappe.whitelist()
-def enqueue_replace_abbr(company, old, new):
-	kwargs = dict(queue="long", company=company, old=old, new=new)
-	frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs)
-
-
-@frappe.whitelist()
-def replace_abbr(company, old, new):
-	new = new.strip()
-	if not new:
-		frappe.throw(_("Abbr can not be blank or space"))
-
-	frappe.only_for("System Manager")
-
-	def _rename_record(doc):
-		parts = doc[0].rsplit(" - ", 1)
-		if len(parts) == 1 or parts[1].lower() == old.lower():
-			frappe.rename_doc(dt, doc[0], parts[0] + " - " + new, force=True)
-
-	def _rename_records(dt):
-		# rename is expensive so let's be economical with memory usage
-		doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company))
-		for d in doc:
-			_rename_record(d)
-	try:
-		frappe.db.auto_commit_on_many_writes = 1
-		for dt in ["Warehouse", "Account", "Cost Center", "Department",
-				"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
-			_rename_records(dt)
-			frappe.db.commit()
-		frappe.db.set_value("Company", company, "abbr", new)
-
-	except Exception:
-		frappe.log_error(title=_('Abbreviation Rename Error'))
-	finally:
-		frappe.db.auto_commit_on_many_writes = 0
-
-
 def get_name_with_abbr(name, company):
 	company_abbr = frappe.get_cached_value('Company',  company,  "abbr")
 	parts = name.split(" - ")
diff --git a/erpnext/setup/doctype/uom/uom.json b/erpnext/setup/doctype/uom/uom.json
index 3a4e7f6..844a11f 100644
--- a/erpnext/setup/doctype/uom/uom.json
+++ b/erpnext/setup/doctype/uom/uom.json
@@ -1,164 +1,82 @@
 {
- "allow_copy": 0,
- "allow_guest_to_view": 0,
+ "actions": [],
  "allow_import": 1,
  "allow_rename": 1,
  "autoname": "field:uom_name",
- "beta": 0,
  "creation": "2013-01-10 16:34:24",
- "custom": 0,
- "docstatus": 0,
  "doctype": "DocType",
  "document_type": "Setup",
- "editable_grid": 0,
+ "engine": "InnoDB",
+ "field_order": [
+  "enabled",
+  "uom_name",
+  "must_be_whole_number"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
    "fieldname": "uom_name",
    "fieldtype": "Data",
-   "hidden": 0,
-   "ignore_user_permissions": 0,
-   "ignore_xss_filter": 0,
-   "in_filter": 0,
-   "in_global_search": 0,
    "in_list_view": 1,
-   "in_standard_filter": 0,
    "label": "UOM Name",
-   "length": 0,
-   "no_copy": 0,
    "oldfieldname": "uom_name",
    "oldfieldtype": "Data",
-   "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": 1
   },
   {
-   "allow_bulk_edit": 0,
-   "allow_in_quick_entry": 0,
-   "allow_on_submit": 0,
-   "bold": 0,
-   "collapsible": 0,
-   "columns": 0,
+   "default": "0",
    "description": "Check this to disallow fractions. (for Nos)",
    "fieldname": "must_be_whole_number",
    "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": "Must be Whole Number",
-   "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
+   "label": "Must be Whole Number"
+  },
+  {
+   "default": "1",
+   "fieldname": "enabled",
+   "fieldtype": "Check",
+   "label": "Enabled"
   }
  ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
  "icon": "fa fa-compass",
  "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-08-29 06:35:56.143361",
+ "links": [],
+ "modified": "2021-10-18 14:07:43.722144",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "UOM",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0,
-   "cancel": 0,
    "create": 1,
    "delete": 1,
    "email": 1,
    "export": 1,
-   "if_owner": 0,
    "import": 1,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
    "role": "Item Manager",
-   "set_user_permissions": 0,
    "share": 1,
-   "submit": 0,
    "write": 1
   },
   {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
    "email": 1,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
-   "role": "Stock Manager",
-   "set_user_permissions": 0,
-   "share": 0,
-   "submit": 0,
-   "write": 0
+   "role": "Stock Manager"
   },
   {
-   "amend": 0,
-   "cancel": 0,
-   "create": 0,
-   "delete": 0,
    "email": 1,
-   "export": 0,
-   "if_owner": 0,
-   "import": 0,
-   "permlevel": 0,
    "print": 1,
    "read": 1,
    "report": 1,
-   "role": "Stock User",
-   "set_user_permissions": 0,
-   "share": 0,
-   "submit": 0,
-   "write": 0
+   "role": "Stock User"
   }
  ],
  "quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
  "show_name_in_global_search": 1,
- "sort_order": "ASC",
- "track_changes": 0,
- "track_seen": 0,
- "track_views": 0
+ "sort_field": "modified",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index faa25df..58a14d2 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -192,7 +192,7 @@
 	default_root_type = 'Liability'
 	root_type = account.get('root_type', default_root_type)
 
-	existing_accounts = frappe.get_list('Account',
+	existing_accounts = frappe.get_all('Account',
 		filters={
 			'company': company_name,
 			'root_type': root_type
@@ -247,7 +247,7 @@
 
 	# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
 	# below the root account
-	root_account = frappe.get_list('Account', {
+	root_account = frappe.get_all('Account', {
 		'is_group': 1,
 		'root_type': root_type,
 		'company': company_name,
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index ef4b050..1412acf 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -1,31 +1,21 @@
 {
- "category": "",
  "charts": [],
- "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Projects Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"HR Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Selling Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Buying Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Support Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Shopping Cart Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Portal Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Manufacturing Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Education Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Hotel Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Domain Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Products Settings\", \"col\": 4}}]",
+ "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Projects Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"HR Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Support Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Shopping Cart Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Portal Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Domain Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Products Settings\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Naming Series\",\"col\":4}}]",
  "creation": "2020-03-12 14:47:51.166455",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "setting",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "ERPNext Settings",
  "links": [],
- "modified": "2021-08-05 12:15:59.052327",
+ "modified": "2021-10-26 21:32:55.323591",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "ERPNext Settings",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
@@ -38,6 +28,14 @@
    "type": "DocType"
   },
   {
+   "color": "Grey",
+   "doc_view": "",
+   "icon": "dot-horizontal",
+   "label": "Naming Series",
+   "link_to": "Naming Series",
+   "type": "DocType"
+  },
+  {
    "icon": "accounting",
    "label": "Accounts Settings",
    "link_to": "Accounts Settings",
diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json
index a4e7ad8..4e1ccf9 100644
--- a/erpnext/setup/workspace/home/home.json
+++ b/erpnext/setup/workspace/home/home.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
- "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports &amp; Masters\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Human Resources\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
+ "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Human Resources\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]",
  "creation": "2020-01-23 13:46:38.833076",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "getting-started",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Home",
  "links": [
   {
@@ -278,15 +271,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-10 15:33:20.704740",
+ "modified": "2021-08-10 15:33:20.704741",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Home",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
index f8a22b0..1164a5d 100644
--- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
+++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py
@@ -44,7 +44,6 @@
 
 	def test_tax_rule_validation(self):
 		frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0")
-		frappe.db.commit()
 
 		cart_settings = self.get_cart_settings()
 		cart_settings.enabled = 1
diff --git a/erpnext/shopping_cart/utils.py b/erpnext/shopping_cart/utils.py
index f412e61..5f0c792 100644
--- a/erpnext/shopping_cart/utils.py
+++ b/erpnext/shopping_cart/utils.py
@@ -1,8 +1,5 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-
 import frappe
 
 from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import (
@@ -18,10 +15,19 @@
 	return False
 
 def set_cart_count(login_manager):
-	role, parties = check_customer_or_supplier()
-	if role == 'Supplier': return
+	# since this is run only on hooks login event
+	# make sure user is already a customer
+	# before trying to set cart count
+	user_is_customer = is_customer()
+	if not user_is_customer:
+		return
+
 	if show_cart_count():
 		from erpnext.shopping_cart.cart import set_cart_count
+
+		# set_cart_count will try to fetch existing cart quotation
+		# or create one if non existent (and create a customer too)
+		# cart count is calculated from this quotation's items
 		set_cart_count()
 
 def clear_cart_count(login_manager):
@@ -32,13 +38,13 @@
 	cart_enabled = is_cart_enabled()
 	context["shopping_cart_enabled"] = cart_enabled
 
-def check_customer_or_supplier():
-	if frappe.session.user:
+def is_customer():
+	if frappe.session.user and frappe.session.user != "Guest":
 		contact_name = frappe.get_value("Contact", {"email_id": frappe.session.user})
 		if contact_name:
 			contact = frappe.get_doc('Contact', contact_name)
 			for link in contact.links:
-				if link.link_doctype in ('Customer', 'Supplier'):
-					return link.link_doctype, link.link_name
+				if link.link_doctype == 'Customer':
+					return True
 
-		return 'Customer', None
+		return False
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 7998930..0a663c2 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -1,8 +1,5 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 # License: GNU General Public License v3. See license.txt
-from __future__ import unicode_literals
-
-import unittest
 
 import frappe
 from frappe.exceptions import ValidationError
@@ -11,9 +8,10 @@
 from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
 from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError, get_batch_no, get_batch_qty
 from erpnext.stock.get_item_details import get_item_details
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestBatch(unittest.TestCase):
+class TestBatch(ERPNextTestCase):
 	def test_item_has_batch_enabled(self):
 		self.assertRaises(ValidationError, frappe.get_doc({
 			"doctype": "Batch",
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 5fbc2d8..4be0415 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -14,51 +14,6 @@
 			self.stock_uom = frappe.get_cached_value('Item', self.item_code, 'stock_uom')
 		self.set_projected_qty()
 
-	def update_stock(self, args, allow_negative_stock=False, via_landed_cost_voucher=False):
-		'''Called from erpnext.stock.utils.update_bin'''
-		self.update_qty(args)
-
-		if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
-			from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
-
-			if not args.get("posting_date"):
-				args["posting_date"] = nowdate()
-
-			if args.get("is_cancelled") and via_landed_cost_voucher:
-				return
-
-			# Reposts only current voucher SL Entries
-			# Updates valuation rate, stock value, stock queue for current transaction
-			update_entries_after({
-				"item_code": self.item_code,
-				"warehouse": self.warehouse,
-				"posting_date": args.get("posting_date"),
-				"posting_time": args.get("posting_time"),
-				"voucher_type": args.get("voucher_type"),
-				"voucher_no": args.get("voucher_no"),
-				"sle_id": args.name,
-				"creation": args.creation
-			}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
-
-			# update qty in future ale and Validate negative qty
-			update_qty_in_future_sle(args, allow_negative_stock)
-
-
-	def update_qty(self, args):
-		# update the stock values (for current quantities)
-		if args.get("voucher_type")=="Stock Reconciliation":
-			self.actual_qty = args.get("qty_after_transaction")
-		else:
-			self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
-
-		self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
-		self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
-		self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
-		self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
-
-		self.set_projected_qty()
-		self.db_update()
-
 	def set_projected_qty(self):
 		self.projected_qty = (flt(self.actual_qty) + flt(self.ordered_qty)
 			+ flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
@@ -143,3 +98,67 @@
 
 def on_doctype_update():
 	frappe.db.add_index("Bin", ["item_code", "warehouse"])
+
+
+def update_stock(bin_name, args, allow_negative_stock=False, via_landed_cost_voucher=False):
+	'''Called from erpnext.stock.utils.update_bin'''
+	update_qty(bin_name, args)
+
+	if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
+		from erpnext.stock.stock_ledger import update_entries_after, update_qty_in_future_sle
+
+		if not args.get("posting_date"):
+			args["posting_date"] = nowdate()
+
+		if args.get("is_cancelled") and via_landed_cost_voucher:
+			return
+
+		# Reposts only current voucher SL Entries
+		# Updates valuation rate, stock value, stock queue for current transaction
+		update_entries_after({
+			"item_code": args.get('item_code'),
+			"warehouse": args.get('warehouse'),
+			"posting_date": args.get("posting_date"),
+			"posting_time": args.get("posting_time"),
+			"voucher_type": args.get("voucher_type"),
+			"voucher_no": args.get("voucher_no"),
+			"sle_id": args.get('name'),
+			"creation": args.get('creation')
+		}, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher)
+
+		# update qty in future sle and Validate negative qty
+		update_qty_in_future_sle(args, allow_negative_stock)
+
+def get_bin_details(bin_name):
+	return frappe.db.get_value('Bin', bin_name, ['actual_qty', 'ordered_qty',
+	'reserved_qty', 'indented_qty', 'planned_qty', 'reserved_qty_for_production',
+	'reserved_qty_for_sub_contract'], as_dict=1)
+
+def update_qty(bin_name, args):
+	bin_details = get_bin_details(bin_name)
+
+	# update the stock values (for current quantities)
+	if args.get("voucher_type")=="Stock Reconciliation":
+		actual_qty = args.get('qty_after_transaction')
+	else:
+		actual_qty = bin_details.actual_qty + flt(args.get("actual_qty"))
+
+	ordered_qty = flt(bin_details.ordered_qty) + flt(args.get("ordered_qty"))
+	reserved_qty = flt(bin_details.reserved_qty) + flt(args.get("reserved_qty"))
+	indented_qty = flt(bin_details.indented_qty) + flt(args.get("indented_qty"))
+	planned_qty = flt(bin_details.planned_qty) + flt(args.get("planned_qty"))
+
+
+	# compute projected qty
+	projected_qty = (flt(actual_qty) + flt(ordered_qty)
+		+ flt(indented_qty) + flt(planned_qty) - flt(reserved_qty)
+		- flt(bin_details.reserved_qty_for_production) - flt(bin_details.reserved_qty_for_sub_contract))
+
+	frappe.db.set_value('Bin', bin_name, {
+		'actual_qty': actual_qty,
+		'ordered_qty': ordered_qty,
+		'reserved_qty': reserved_qty,
+		'indented_qty': indented_qty,
+		'planned_qty': planned_qty,
+		'projected_qty': projected_qty
+	})
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index 9bf142c..ad1b3b4 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -395,8 +395,7 @@
    "fieldtype": "Link",
    "label": "Billing Address Name",
    "options": "Address",
-   "print_hide": 1,
-   "read_only": 1
+   "print_hide": 1
   },
   {
    "fieldname": "tax_id",
@@ -1309,7 +1308,7 @@
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-09-28 13:10:09.761714",
+ "modified": "2021-10-08 14:29:13.428984",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 5542cd0..f75b52c 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -185,7 +185,6 @@
 			if not d['warehouse'] and frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
 				frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
 
-
 	def update_current_stock(self):
 		if self.get("_action") and self._action != "update_after_submit":
 			for d in self.get('items'):
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 7fda94b..f58b586 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -5,7 +5,6 @@
 from __future__ import unicode_literals
 
 import json
-import unittest
 
 import frappe
 from frappe.utils import cstr, flt, nowdate, nowtime
@@ -37,9 +36,10 @@
 )
 from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestDeliveryNote(unittest.TestCase):
+class TestDeliveryNote(ERPNextTestCase):
 	def test_over_billing_against_dn(self):
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index b05090a..a96c299 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -468,7 +468,7 @@
    "width": "100px"
   },
   {
-   "depends_on": "eval:parent.is_internal_customer",
+   "depends_on": "eval:parent.is_internal_customer || doc.target_warehouse",
    "fieldname": "target_warehouse",
    "fieldtype": "Link",
    "hidden": 1,
@@ -759,7 +759,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-02-23 01:04:08.588104",
+ "modified": "2021-10-05 12:12:44.018872",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
@@ -767,4 +767,4 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC"
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
index c9081c9..c6ff73e 100644
--- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
+++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py
@@ -14,11 +14,12 @@
 	make_expense_claim,
 	notify_customers,
 )
-from erpnext.tests.utils import create_test_contact_and_address
+from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
 
 
-class TestDeliveryTrip(unittest.TestCase):
+class TestDeliveryTrip(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		driver = create_driver()
 		create_vehicle()
 		create_delivery_notification()
@@ -32,6 +33,7 @@
 		frappe.db.sql("delete from `tabVehicle`")
 		frappe.db.sql("delete from `tabEmail Template`")
 		frappe.db.sql("delete from `tabDelivery Trip`")
+		return super().tearDown()
 
 	def test_expense_claim_fields_are_fetched_properly(self):
 		expense_claim = make_expense_claim(self.delivery_trip.name)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 768e5ea..8cc9f74 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -181,6 +181,8 @@
 				"doctype": "Item Price",
 				"price_list": price_list,
 				"item_code": self.name,
+				"uom": self.stock_uom,
+				"brand": self.brand,
 				"currency": erpnext.get_default_currency(),
 				"price_list_rate": self.standard_rate
 			})
@@ -634,9 +636,21 @@
 				_("An Item Group exists with same name, please change the item name or rename the item group"))
 
 	def update_item_price(self):
-		frappe.db.sql("""update `tabItem Price` set item_name=%s,
-			item_description=%s, brand=%s where item_code=%s""",
-					(self.item_name, self.description, self.brand, self.name))
+		frappe.db.sql("""
+				UPDATE `tabItem Price`
+				SET
+					item_name=%(item_name)s,
+					item_description=%(item_description)s,
+					brand=%(brand)s
+				WHERE item_code=%(item_code)s
+			""",
+			dict(
+				item_name=self.item_name,
+				item_description=self.description,
+				brand=self.brand,
+				item_code=self.name
+			)
+		)
 
 	def on_trash(self):
 		super(Item, self).on_trash()
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index e911d35..9198272 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -4,7 +4,6 @@
 from __future__ import unicode_literals
 
 import json
-import unittest
 
 import frappe
 from frappe.test_runner import make_test_objects
@@ -25,7 +24,7 @@
 )
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
 from erpnext.stock.get_item_details import get_item_details
-from erpnext.tests.utils import change_settings
+from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 test_ignore = ["BOM"]
 test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"]
@@ -53,8 +52,9 @@
 
 	return item
 
-class TestItem(unittest.TestCase):
+class TestItem(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		frappe.flags.attribute_values = None
 
 	def get_item(self, idx):
diff --git a/erpnext/stock/doctype/item_alternative/test_item_alternative.py b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
index 2be8ef7..af6cc47 100644
--- a/erpnext/stock/doctype/item_alternative/test_item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/test_item_alternative.py
@@ -4,7 +4,6 @@
 from __future__ import unicode_literals
 
 import json
-import unittest
 
 import frappe
 from frappe.utils import flt
@@ -21,10 +20,12 @@
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
 	create_stock_reconciliation,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemAlternative(unittest.TestCase):
+class TestItemAlternative(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		make_items()
 
 	def test_alternative_item_for_subcontract_rm(self):
diff --git a/erpnext/stock/doctype/item_attribute/test_item_attribute.py b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
index fc809f4..2cd711b 100644
--- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py
+++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py
@@ -3,17 +3,17 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 test_records = frappe.get_test_records('Item Attribute')
 
 from erpnext.stock.doctype.item_attribute.item_attribute import ItemAttributeIncrementError
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemAttribute(unittest.TestCase):
+class TestItemAttribute(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		if frappe.db.exists("Item Attribute", "_Test_Length"):
 			frappe.delete_doc("Item Attribute", "_Test_Length")
 
diff --git a/erpnext/stock/doctype/item_price/test_item_price.py b/erpnext/stock/doctype/item_price/test_item_price.py
index 5ed8092..3a51fbb 100644
--- a/erpnext/stock/doctype/item_price/test_item_price.py
+++ b/erpnext/stock/doctype/item_price/test_item_price.py
@@ -3,17 +3,17 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.test_runner import make_test_records_for_doctype
 
 from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
 from erpnext.stock.get_item_details import get_price_list_rate_for, process_args
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestItemPrice(unittest.TestCase):
+class TestItemPrice(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		frappe.db.sql("delete from `tabItem Price`")
 		make_test_records_for_doctype("Item Price", force=True)
 
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 58a72f7..339eaaa 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
@@ -4,8 +4,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.utils import flt
 
@@ -16,9 +14,10 @@
 	get_gl_entries,
 	make_purchase_receipt,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestLandedCostVoucher(unittest.TestCase):
+class TestLandedCostVoucher(ERPNextTestCase):
 	def test_landed_cost_voucher(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index cf98b19..17df977 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -296,7 +296,7 @@
 
 		return d.ordered_qty < d.stock_qty and child_filter
 
-	doclist = get_mapped_doc("Material Request", source_name, 	{
+	doclist = get_mapped_doc("Material Request", source_name, {
 		"Material Request": {
 			"doctype": "Purchase Order",
 			"validation": {
@@ -323,7 +323,7 @@
 
 @frappe.whitelist()
 def make_request_for_quotation(source_name, target_doc=None):
-	doclist = get_mapped_doc("Material Request", source_name, 	{
+	doclist = get_mapped_doc("Material Request", source_name, {
 		"Material Request": {
 			"doctype": "Request for Quotation",
 			"validation": {
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index 5c2ac25..f66a228 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -6,8 +6,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.utils import flt, today
 
@@ -18,9 +16,10 @@
 	make_supplier_quotation,
 	raise_work_orders,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestMaterialRequest(unittest.TestCase):
+class TestMaterialRequest(ERPNextTestCase):
 	def test_make_purchase_order(self):
 		mr = frappe.copy_doc(test_records[0]).insert()
 
diff --git a/erpnext/stock/doctype/packing_slip/test_packing_slip.py b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
index 193adfc..c70cba6 100644
--- a/erpnext/stock/doctype/packing_slip/test_packing_slip.py
+++ b/erpnext/stock/doctype/packing_slip/test_packing_slip.py
@@ -6,6 +6,8 @@
 import unittest
 
 # test_records = frappe.get_test_records('Packing Slip')
+from erpnext.tests.utils import ERPNextTestCase
+
 
 class TestPackingSlip(unittest.TestCase):
 	pass
diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json
index 2146793..c604c71 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.json
+++ b/erpnext/stock/doctype/pick_list/pick_list.json
@@ -18,7 +18,9 @@
   "get_item_locations",
   "section_break_6",
   "locations",
-  "amended_from"
+  "amended_from",
+  "print_settings_section",
+  "group_same_items"
  ],
  "fields": [
   {
@@ -110,14 +112,28 @@
    "options": "STO-PICK-.YYYY.-",
    "reqd": 1,
    "set_only_once": 1
+  },
+  {
+   "fieldname": "print_settings_section",
+   "fieldtype": "Section Break",
+   "label": "Print Settings"
+  },
+  {
+   "allow_on_submit": 1,
+   "default": "0",
+   "fieldname": "group_same_items",
+   "fieldtype": "Check",
+   "label": "Group Same Items",
+   "print_hide": 1
   }
  ],
  "is_submittable": 1,
  "links": [],
- "modified": "2020-03-17 11:38:41.932875",
+ "modified": "2021-10-05 15:08:40.369957",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Pick List",
+ "naming_rule": "By \"Naming Series\" field",
  "owner": "Administrator",
  "permissions": [
   {
@@ -184,4 +200,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index dffbe80..4c02f3d 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -2,10 +2,8 @@
 # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
-from __future__ import unicode_literals
-
 import json
-from collections import OrderedDict
+from collections import OrderedDict, defaultdict
 
 import frappe
 from frappe import _
@@ -121,6 +119,34 @@
 				and (self.for_qty is None or self.for_qty == 0):
 			frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
 
+	def before_print(self, settings=None):
+		if self.get("group_same_items"):
+			self.group_similar_items()
+
+	def group_similar_items(self):
+		group_item_qty = defaultdict(float)
+		group_picked_qty = defaultdict(float)
+
+		for item in self.locations:
+			group_item_qty[(item.item_code, item.warehouse)] +=  item.qty
+			group_picked_qty[(item.item_code, item.warehouse)] += item.picked_qty
+
+		duplicate_list = []
+		for item in self.locations:
+			if (item.item_code, item.warehouse) in group_item_qty:
+				item.qty = group_item_qty[(item.item_code, item.warehouse)]
+				item.picked_qty = group_picked_qty[(item.item_code, item.warehouse)]
+				item.stock_qty = group_item_qty[(item.item_code, item.warehouse)]
+				del group_item_qty[(item.item_code, item.warehouse)]
+			else:
+				duplicate_list.append(item)
+
+		for item in duplicate_list:
+			self.remove(item)
+
+		for idx, item in enumerate(self.locations, start=1):
+			item.idx = idx
+
 
 def validate_item_locations(pick_list):
 	if not pick_list.locations:
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index aa710ad..58b46e1 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -3,9 +3,8 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
+from frappe import _dict
 
 test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
 
@@ -15,9 +14,10 @@
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
 	EmptyStockReconciliationItemsError,
 )
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPickList(unittest.TestCase):
+class TestPickList(ERPNextTestCase):
 
 	def test_pick_list_picks_warehouse_for_each_item(self):
 		try:
@@ -357,6 +357,39 @@
 		sales_order.cancel()
 		purchase_receipt.cancel()
 
+	def test_pick_list_grouping_before_print(self):
+		def _compare_dicts(a, b):
+			"compare dicts but ignore missing keys in `a`"
+			for key, value in a.items():
+				self.assertEqual(b.get(key), value, msg=f"{key} doesn't match")
+
+		# nothing should be grouped
+		pl = frappe.get_doc(doctype="Pick List", group_same_items=True, locations=[
+			_dict(item_code="A", warehouse="X", qty=1, picked_qty=2),
+			_dict(item_code="B", warehouse="X", qty=1, picked_qty=2),
+			_dict(item_code="A", warehouse="Y", qty=1, picked_qty=2),
+			_dict(item_code="B", warehouse="Y", qty=1, picked_qty=2),
+		])
+		pl.before_print()
+		self.assertEqual(len(pl.locations), 4)
+
+		# grouping should halve the number of items
+		pl = frappe.get_doc(doctype="Pick List", group_same_items=True, locations=[
+			_dict(item_code="A", warehouse="X", qty=5, picked_qty=1),
+			_dict(item_code="B", warehouse="Y", qty=4, picked_qty=2),
+			_dict(item_code="A", warehouse="X", qty=3, picked_qty=2),
+			_dict(item_code="B", warehouse="Y", qty=2, picked_qty=2),
+		])
+		pl.before_print()
+		self.assertEqual(len(pl.locations), 2)
+
+		expected_items = [
+			_dict(item_code="A", warehouse="X", qty=8, picked_qty=3),
+			_dict(item_code="B", warehouse="Y", qty=6, picked_qty=4),
+		]
+		for expected_item, created_item in zip(expected_items, pl.locations):
+			_compare_dicts(expected_item, created_item)
+
 	# def test_pick_list_skips_items_in_expired_batch(self):
 	# 	pass
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 07a568d..47c8df9 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -842,7 +842,8 @@
 			"doctype": "Stock Entry Detail",
 			"field_map": {
 				"warehouse": "s_warehouse",
-				"parent": "reference_purchase_receipt"
+				"parent": "reference_purchase_receipt",
+				"batch_no": "batch_no"
 			},
 		},
 	}, target_doc, set_missing_values)
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 044856c..de17744 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -17,9 +17,10 @@
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPurchaseReceipt(unittest.TestCase):
+class TestPurchaseReceipt(ERPNextTestCase):
 	def setUp(self):
 		frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
 
diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
index 0aa7610..c25bca9 100644
--- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py
@@ -3,8 +3,6 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 from erpnext.stock.doctype.batch.test_batch import make_new_batch
@@ -13,9 +11,10 @@
 from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.get_item_details import get_conversion_factor
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestPutawayRule(unittest.TestCase):
+class TestPutawayRule(ERPNextTestCase):
 	def setUp(self):
 		if not frappe.db.exists("Item", "_Rice"):
 			make_item("_Rice", {
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index f5d076a..308c628 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -1,8 +1,6 @@
 # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
 # See license.txt
 
-import unittest
-
 import frappe
 from frappe.utils import nowdate
 
@@ -15,12 +13,14 @@
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.tests.utils import ERPNextTestCase
 
 # test_records = frappe.get_test_records('Quality Inspection')
 
 
-class TestQualityInspection(unittest.TestCase):
+class TestQualityInspection(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		create_item("_Test Item with QA")
 		frappe.db.set_value(
 			"Item", "_Test Item with QA", "inspection_required_before_delivery", 1
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 82d8aae..a9254fb 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -611,7 +611,9 @@
 
 	return reserved_sr_nos
 
-def fetch_serial_numbers(filters, qty, do_not_include=[]):
+def fetch_serial_numbers(filters, qty, do_not_include=None):
+	if do_not_include is None:
+		do_not_include = []
 	batch_join_selection = ""
 	batch_no_condition = ""
 	batch_nos = filters.get("batch_no")
diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py
index 818c163..570f22e 100644
--- a/erpnext/stock/doctype/serial_no/test_serial_no.py
+++ b/erpnext/stock/doctype/serial_no/test_serial_no.py
@@ -6,8 +6,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 
 from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
@@ -20,9 +18,10 @@
 test_records = frappe.get_test_records('Serial No')
 
 from erpnext.stock.doctype.serial_no.serial_no import *
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestSerialNo(unittest.TestCase):
+class TestSerialNo(ERPNextTestCase):
 	def test_cannot_create_direct(self):
 		frappe.delete_doc_if_exists("Serial No", "_TCSER0001")
 
@@ -185,14 +184,14 @@
 
 		se = frappe.copy_doc(test_records[0])
 		se.get("items")[0].item_code = item_code
-		se.get("items")[0].qty = 3
-		se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3  "
-		se.get("items")[0].transfer_qty = 3
+		se.get("items")[0].qty = 4
+		se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3  , _TS4 - 2021"
+		se.get("items")[0].transfer_qty = 4
 		se.set_stock_entry_type()
 		se.insert()
 		se.submit()
 
-		self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3")
+		self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3\n_TS4 - 2021")
 
 		frappe.db.rollback()
 
diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py
index 9914cf8..dcd0b7c 100644
--- a/erpnext/stock/doctype/shipment/test_shipment.py
+++ b/erpnext/stock/doctype/shipment/test_shipment.py
@@ -3,15 +3,15 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
 from datetime import date, timedelta
 
 import frappe
 
 from erpnext.stock.doctype.delivery_note.delivery_note import make_shipment
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestShipment(unittest.TestCase):
+class TestShipment(ERPNextTestCase):
 	def test_shipment_from_delivery_note(self):
 		delivery_note = create_test_delivery_note()
 		delivery_note.submit()
@@ -47,7 +47,6 @@
 		}
 	)
 	delivery_note.insert()
-	frappe.db.commit()
 	return delivery_note
 
 
@@ -91,7 +90,6 @@
 		}
 	)
 	shipment.insert()
-	frappe.db.commit()
 	return shipment
 
 
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 2463a21..2651407 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -142,6 +142,7 @@
    "oldfieldtype": "Data",
    "print_width": "150px",
    "read_only": 1,
+   "search_index": 1,
    "width": "150px"
   },
   {
@@ -316,7 +317,7 @@
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-09-07 11:10:35.318872",
+ "modified": "2021-10-08 13:42:51.857631",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Ledger Entry",
@@ -338,4 +339,4 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC"
-}
\ No newline at end of file
+}
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 caa1d42..2cf71ac 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -181,4 +181,4 @@
 
 	frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
 	frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
-	frappe.db.add_index("Stock Ledger Entry", ["voucher_detail_no"])
+	frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse")
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 61bae49..ff33c27 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -3,8 +3,6 @@
 # See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.core.page.permission_manager.permission_manager import reset
 from frappe.utils import add_days, today
@@ -21,9 +19,10 @@
 	create_stock_reconciliation,
 )
 from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockLedgerEntry(unittest.TestCase):
+class TestStockLedgerEntry(ERPNextTestCase):
 	def setUp(self):
 		items = create_items()
 		reset('Stock Entry')
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 8647bee..415ac5e 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -6,8 +6,6 @@
 
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.utils import add_days, flt, nowdate, nowtime, random_string
 
@@ -22,12 +20,13 @@
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
-from erpnext.tests.utils import change_settings
+from erpnext.tests.utils import ERPNextTestCase, change_settings
 
 
-class TestStockReconciliation(unittest.TestCase):
+class TestStockReconciliation(ERPNextTestCase):
 	@classmethod
 	def setUpClass(self):
+		super().setUpClass()
 		create_batch_or_serial_no_items()
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 
@@ -372,7 +371,6 @@
 		"""
 		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
 		from erpnext.stock.stock_ledger import NegativeStockError
-		frappe.db.commit()
 
 		item_code = "Backdated-Reco-Cancellation-Item"
 		warehouse = "_Test Warehouse - _TC"
@@ -395,10 +393,6 @@
 		repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
 		self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
 
-		# teardown
-		frappe.db.rollback()
-
-
 	def test_valid_batch(self):
 		create_batch_item_with_batch("Testing Batch Item 1", "001")
 		create_batch_item_with_batch("Testing Batch Item 2", "002")
diff --git a/erpnext/stock/doctype/stock_settings/test_stock_settings.py b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
index 7e80904..bf8ac5d 100644
--- a/erpnext/stock/doctype/stock_settings/test_stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/test_stock_settings.py
@@ -7,9 +7,12 @@
 
 import frappe
 
+from erpnext.tests.utils import ERPNextTestCase
 
-class TestStockSettings(unittest.TestCase):
+
+class TestStockSettings(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		frappe.db.set_value("Stock Settings", None, "clean_description_html", 0)
 
 	def test_settings(self):
diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py
index 1ca7181..98317ec 100644
--- a/erpnext/stock/doctype/warehouse/test_warehouse.py
+++ b/erpnext/stock/doctype/warehouse/test_warehouse.py
@@ -2,8 +2,6 @@
 # License: GNU General Public License v3. See license.txt
 from __future__ import unicode_literals
 
-import unittest
-
 import frappe
 from frappe.test_runner import make_test_records
 from frappe.utils import cint
@@ -12,11 +10,13 @@
 from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.tests.utils import ERPNextTestCase
 
 test_records = frappe.get_test_records('Warehouse')
 
-class TestWarehouse(unittest.TestCase):
+class TestWarehouse(ERPNextTestCase):
 	def setUp(self):
+		super().setUp()
 		if not frappe.get_value('Item', '_Test Item'):
 			make_test_records('Item')
 
diff --git a/erpnext/stock/form_tour/material_request/material_request.json b/erpnext/stock/form_tour/material_request/material_request.json
new file mode 100644
index 0000000..145b4a0
--- /dev/null
+++ b/erpnext/stock/form_tour/material_request/material_request.json
@@ -0,0 +1,97 @@
+{
+ "creation": "2021-07-29 12:32:08.929900",
+ "docstatus": 0,
+ "doctype": "Form Tour",
+ "idx": 0,
+ "is_standard": 1,
+ "modified": "2021-10-05 13:11:13.119453",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Material Request",
+ "owner": "Administrator",
+ "reference_doctype": "Material Request",
+ "save_on_complete": 1,
+ "steps": [
+  {
+   "description": "The purpose of the material request can be selected here. For now select \"Purchase\" as the purpose.",
+   "field": "",
+   "fieldname": "material_request_type",
+   "fieldtype": "Select",
+   "has_next_condition": 1,
+   "is_table_field": 0,
+   "label": "Purpose",
+   "next_step_condition": "eval: doc.material_request_type == \"Purchase\"",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Purpose"
+  },
+  {
+   "description": "Set the \"Required By\" date for the materials. This sets the \"Required By\" date for all the items.",
+   "field": "",
+   "fieldname": "schedule_date",
+   "fieldtype": "Date",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Required By",
+   "next_step_condition": "",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Required By"
+  },
+  {
+   "description": "Setting the target warehouse sets it for all the items.",
+   "field": "",
+   "fieldname": "set_warehouse",
+   "fieldtype": "Link",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Set Target Warehouse",
+   "next_step_condition": "",
+   "parent_field": "",
+   "position": "Left",
+   "title": "Target Warehouse"
+  },
+  {
+   "description": "Items table",
+   "field": "",
+   "fieldname": "items",
+   "fieldtype": "Table",
+   "has_next_condition": 0,
+   "is_table_field": 0,
+   "label": "Items",
+   "parent_field": "",
+   "position": "Bottom",
+   "title": "Items"
+  },
+  {
+   "child_doctype": "Material Request Item",
+   "description": "Select an Item code. Item details will be fetched automatically.",
+   "field": "",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "has_next_condition": 1,
+   "is_table_field": 1,
+   "label": "Item Code",
+   "next_step_condition": "eval: doc.item_code",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Right",
+   "title": "Item Code"
+  },
+  {
+   "child_doctype": "Material Request Item",
+   "description": "Enter the required quantity for the material.",
+   "field": "",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "has_next_condition": 0,
+   "is_table_field": 1,
+   "label": "Quantity",
+   "parent_field": "",
+   "parent_fieldname": "items",
+   "position": "Bottom",
+   "title": "Quantity"
+  }
+ ],
+ "title": "Material Request"
+}
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 19597c3..e0190b6 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -89,7 +89,13 @@
 		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, args.company))
+		if doc and doc.get('doctype') == 'Purchase Order':
+			# calculate company_total_stock only for po
+			bin_details = get_bin_details(args.item_code, out.warehouse, args.company)
+		else:
+			bin_details = get_bin_details(args.item_code, out.warehouse)
+
+		out.update(bin_details)
 
 	# update args with out, if key or value not exists
 	for key, value in iteritems(out):
@@ -382,7 +388,7 @@
 
 	return out
 
-def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
+def get_item_warehouse(item, args, overwrite_warehouse, defaults=None):
 	if not defaults:
 		defaults = frappe._dict({
 			'item_defaults' : get_item_defaults(item.name, args.company),
@@ -485,8 +491,9 @@
 			"item_tax_template": None
 		}
 	"""
-	item_tax_template = args.get("item_tax_template")
-	item_tax_template = _get_item_tax_template(args, item.taxes, out)
+	item_tax_template = None
+	if item.taxes:
+		item_tax_template = _get_item_tax_template(args, item.taxes, out)
 
 	if not item_tax_template:
 		item_group = item.item_group
@@ -502,17 +509,17 @@
 	taxes_with_no_validity = []
 
 	for tax in taxes:
-		tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company')
-		if (tax.valid_from or tax.maximum_net_rate) and tax_company == args['company']:
-			# In purchase Invoice first preference will be given to supplier invoice date
-			# if supplier date is not present then posting date
-			validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
+		tax_company = frappe.get_cached_value("Item Tax Template", tax.item_tax_template, 'company')
+		if tax_company == args['company']:
+			if (tax.valid_from or tax.maximum_net_rate):
+				# In purchase Invoice first preference will be given to supplier invoice date
+				# if supplier date is not present then posting date
+				validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
 
-			if getdate(tax.valid_from) <= getdate(validation_date) \
-				and is_within_valid_range(args, tax):
-				taxes_with_validity.append(tax)
-		else:
-			if tax_company == args['company']:
+				if getdate(tax.valid_from) <= getdate(validation_date) \
+					and is_within_valid_range(args, tax):
+					taxes_with_validity.append(tax)
+			else:
 				taxes_with_no_validity.append(tax)
 
 	if taxes_with_validity:
@@ -890,8 +897,7 @@
 				res[fieldname] = pos_profile.get(fieldname)
 
 		if res.get("warehouse"):
-			res.actual_qty = get_bin_details(args.item_code,
-				res.warehouse).get("actual_qty")
+			res.actual_qty = get_bin_details(args.item_code, res.warehouse).get("actual_qty")
 
 	return res
 
diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py
index 3cd4cd2..7c6fbfd 100644
--- a/erpnext/stock/reorder_item.py
+++ b/erpnext/stock/reorder_item.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 
 import json
+from math import ceil
 
 import frappe
 from frappe import _
@@ -149,11 +150,16 @@
 							conversion_factor = frappe.db.get_value("UOM Conversion Detail",
 								{'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0
 
+					must_be_whole_number = frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True)
+					qty = d.reorder_qty / conversion_factor
+					if must_be_whole_number:
+						qty = ceil(qty)
+
 					mr.append("items", {
 						"doctype": "Material Request Item",
 						"item_code": d.item_code,
 						"schedule_date": add_days(nowdate(),cint(item.lead_time_days)),
-						"qty": d.reorder_qty / conversion_factor,
+						"qty": qty,
 						"uom": uom,
 						"stock_uom": item.stock_uom,
 						"warehouse": d.warehouse,
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
index 21e1205..32df585 100644
--- a/erpnext/stock/report/stock_analytics/test_stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -5,9 +5,10 @@
 
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
+from erpnext.tests.utils import ERPNextTestCase
 
 
-class TestStockAnalyticsReport(unittest.TestCase):
+class TestStockAnalyticsReport(ERPNextTestCase):
 	def test_get_period_date_ranges(self):
 
 		filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index fc5d5c1..bb53c55 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -202,7 +202,9 @@
 
 		value_diff = flt(d.stock_value_difference)
 
-		if d.posting_date < from_date:
+		if d.posting_date < from_date or (d.posting_date == from_date
+			and d.voucher_type == "Stock Reconciliation" and
+			frappe.db.get_value("Stock Reconciliation", d.voucher_no, "purpose") == "Opening Stock"):
 			qty_dict.opening_qty += qty_diff
 			qty_dict.opening_val += value_diff
 
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 1ea58fe..4e20b47 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -21,7 +21,7 @@
 	items = get_items(filters)
 	sl_entries = get_stock_ledger_entries(filters, items)
 	item_details = get_item_details(items, sl_entries, include_uom)
-	opening_row = get_opening_balance(filters, columns)
+	opening_row = get_opening_balance(filters, columns, sl_entries)
 	precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
 
 	data = []
@@ -218,7 +218,7 @@
 	return "and {}".format(" and ".join(conditions)) if conditions else ""
 
 
-def get_opening_balance(filters, columns):
+def get_opening_balance(filters, columns, sl_entries):
 	if not (filters.item_code and filters.warehouse and filters.from_date):
 		return
 
@@ -230,6 +230,15 @@
 		"posting_time": "00:00:00"
 	})
 
+	# check if any SLEs are actually Opening Stock Reconciliation
+	for sle in sl_entries:
+		if (sle.get("voucher_type") == "Stock Reconciliation"
+			and sle.get("date").split()[0] == filters.from_date
+			and frappe.db.get_value("Stock Reconciliation", sle.voucher_no, "purpose") == "Opening Stock"
+		):
+			last_entry = sle
+			sl_entries.remove(sle)
+
 	row = {
 		"item_code": _("'Opening'"),
 		"qty_after_transaction": last_entry.get("qty_after_transaction", 0),
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 1b5b792..bdbec52 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -13,8 +13,8 @@
 
 import erpnext
 from erpnext.stock.utils import (
-	get_bin,
 	get_incoming_outgoing_rate_for_cancel,
+	get_or_make_bin,
 	get_valuation_method,
 )
 
@@ -123,12 +123,11 @@
 		(now(), frappe.session.user, voucher_type, voucher_no))
 
 def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
-	args.update({"doctype": "Stock Ledger Entry"})
+	args["doctype"] = "Stock Ledger Entry"
 	sle = frappe.get_doc(args)
 	sle.flags.ignore_permissions = 1
 	sle.allow_negative_stock=allow_negative_stock
 	sle.via_landed_cost_voucher = via_landed_cost_voucher
-	sle.insert()
 	sle.submit()
 	return sle
 
@@ -805,14 +804,13 @@
 	def update_bin(self):
 		# update bin for each warehouse
 		for warehouse, data in iteritems(self.data):
-			bin_doc = get_bin(self.item_code, warehouse)
-			bin_doc.update({
+			bin_record = get_or_make_bin(self.item_code, warehouse)
+
+			frappe.db.set_value('Bin', bin_record, {
 				"valuation_rate": data.valuation_rate,
 				"actual_qty": data.qty_after_transaction,
 				"stock_value": data.stock_value
 			})
-			bin_doc.flags.via_stock_ledger_entry = True
-			bin_doc.save(ignore_permissions=True)
 
 
 def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
@@ -918,7 +916,7 @@
 		company = erpnext.get_default_company()
 
 	last_valuation_rate = frappe.db.sql("""select valuation_rate
-		from `tabStock Ledger Entry`
+		from `tabStock Ledger Entry` force index (item_warehouse)
 		where
 			item_code = %s
 			AND warehouse = %s
@@ -929,7 +927,7 @@
 	if not last_valuation_rate:
 		# Get valuation rate from last sle for the item against any warehouse
 		last_valuation_rate = frappe.db.sql("""select valuation_rate
-			from `tabStock Ledger Entry`
+			from `tabStock Ledger Entry` force index (item_code)
 			where
 				item_code = %s
 				AND valuation_rate > 0
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index aeb06e9..c4a0497 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -180,12 +180,27 @@
 	bin_obj.flags.ignore_permissions = True
 	return bin_obj
 
+def get_or_make_bin(item_code, warehouse) -> str:
+	bin_record = frappe.db.get_value('Bin', {'item_code': item_code, 'warehouse': warehouse})
+
+	if not bin_record:
+		bin_obj = frappe.get_doc({
+			"doctype": "Bin",
+			"item_code": item_code,
+			"warehouse": warehouse,
+		})
+		bin_obj.flags.ignore_permissions = 1
+		bin_obj.insert()
+		bin_record = bin_obj.name
+
+	return bin_record
+
 def update_bin(args, allow_negative_stock=False, via_landed_cost_voucher=False):
+	from erpnext.stock.doctype.bin.bin import update_stock
 	is_stock_item = frappe.get_cached_value('Item', args.get("item_code"), 'is_stock_item')
 	if is_stock_item:
-		bin = get_bin(args.get("item_code"), args.get("warehouse"))
-		bin.update_stock(args, allow_negative_stock, via_landed_cost_voucher)
-		return bin
+		bin_record = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
+		update_stock(bin_record, args, allow_negative_stock, via_landed_cost_voucher)
 	else:
 		frappe.msgprint(_("Item {0} ignored since it is not a stock item").format(args.get("item_code")))
 
diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json
index 26d10ce..9c80515 100644
--- a/erpnext/stock/workspace/stock/stock.json
+++ b/erpnext/stock/workspace/stock/stock.json
@@ -1,6 +1,4 @@
 {
- "cards_label": "Masters & Reports",
- "category": "",
  "charts": [
   {
    "chart_name": "Warehouse wise Stock Value"
@@ -8,18 +6,12 @@
  ],
  "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Stock\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Receipt\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Delivery Note\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Masters & Reports\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Transactions\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Serial No and Batch\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Incorrect Data Report\", \"col\": 4}}]",
  "creation": "2020-03-02 15:43:10.096528",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "stock",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Stock",
  "links": [
   {
@@ -764,15 +756,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:02.361509",
+ "modified": "2021-08-05 12:16:02.361519",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock",
- "onboarding": "Stock",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
@@ -831,6 +820,5 @@
    "type": "Dashboard"
   }
  ],
- "shortcuts_label": "Quick Access",
  "title": "Stock"
 }
\ No newline at end of file
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 7d7399d..0fe1068 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -228,7 +228,7 @@
 def set_first_response_time(communication, method):
 	if communication.get('reference_doctype') == "Issue":
 		issue = get_parent_doc(communication)
-		if is_first_response(issue):
+		if is_first_response(issue) and issue.service_level_agreement:
 			first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
 			issue.db_set("first_response_time", first_response_time)
 
diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json
index 5d3d3ac..bf1daa1 100644
--- a/erpnext/support/doctype/support_settings/support_settings.json
+++ b/erpnext/support/doctype/support_settings/support_settings.json
@@ -37,7 +37,6 @@
   },
   {
    "default": "7",
-   "description": "Auto close Issue after 7 days",
    "fieldname": "close_issue_after_days",
    "fieldtype": "Int",
    "label": "Close Issue After Days"
@@ -164,7 +163,7 @@
  ],
  "issingle": 1,
  "links": [],
- "modified": "2020-06-11 13:08:38.473616",
+ "modified": "2021-10-14 13:08:38.473616",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Support Settings",
@@ -185,4 +184,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/support/workspace/support/support.json b/erpnext/support/workspace/support/support.json
index 4c5829d..d68c7c7 100644
--- a/erpnext/support/workspace/support/support.json
+++ b/erpnext/support/workspace/support/support.json
@@ -1,20 +1,13 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Issue\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Maintenance Visit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Issues\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Warranty\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]",
  "creation": "2020-03-02 15:48:23.224699",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "icon": "support",
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Support",
  "links": [
   {
@@ -176,15 +169,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:02.699923",
+ "modified": "2021-08-05 12:16:02.699924",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "Support",
- "onboarding": "",
  "owner": "Administrator",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index a3cab4b..91df548 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -2,6 +2,7 @@
 # License: GNU General Public License v3. See license.txt
 
 import copy
+import unittest
 from contextlib import contextmanager
 from typing import Any, Dict, NewType, Optional
 
@@ -12,6 +13,21 @@
 ReportName = NewType("ReportName", str)
 
 
+class ERPNextTestCase(unittest.TestCase):
+	"""A sane default test class for ERPNext tests."""
+
+
+	@classmethod
+	def setUpClass(cls) -> None:
+		frappe.db.commit()
+		return super().setUpClass()
+
+	@classmethod
+	def tearDownClass(cls) -> None:
+		frappe.db.rollback()
+		return super().tearDownClass()
+
+
 def create_test_contact_and_address():
 	frappe.db.sql('delete from tabContact')
 	frappe.db.sql('delete from `tabContact Email`')
diff --git a/erpnext/utilities/workspace/utilities/utilities.json b/erpnext/utilities/workspace/utilities/utilities.json
index 4ad4afb..02a8af5 100644
--- a/erpnext/utilities/workspace/utilities/utilities.json
+++ b/erpnext/utilities/workspace/utilities/utilities.json
@@ -1,19 +1,12 @@
 {
- "category": "",
  "charts": [],
  "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Video\", \"col\": 4}}]",
  "creation": "2020-09-10 12:21:22.335307",
- "developer_mode_only": 0,
- "disable_user_customization": 0,
  "docstatus": 0,
  "doctype": "Workspace",
- "extends": "",
- "extends_another_page": 0,
  "for_user": "",
  "hide_custom": 0,
  "idx": 0,
- "is_default": 0,
- "is_standard": 0,
  "label": "Utilities",
  "links": [
   {
@@ -47,15 +40,12 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:16:03.350804",
+ "modified": "2021-08-05 12:16:03.350805",
  "modified_by": "Administrator",
  "module": "Utilities",
  "name": "Utilities",
- "onboarding": "",
  "owner": "user@erpnext.com",
  "parent_page": "",
- "pin_to_bottom": 0,
- "pin_to_top": 0,
  "public": 1,
  "restrict_to_domain": "",
  "roles": [],