Merge pull request #27994 from rtdany10/patch-8

fix: incorrect VAT Amount in UAT VAT 201 report
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 166e98a..0000000
--- a/.github/helper/semgrep_rules/frappe_correctness.yml
+++ /dev/null
@@ -1,151 +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
-
-
-- id: frappe-manual-commit
-  patterns:
-    - pattern: frappe.db.commit()
-    - pattern-not-inside: |
-        try:
-          ...
-        except ...:
-          ...
-  message: |
-    Manually commiting a transaction is highly discouraged. Read about the transaction model implemented by Frappe Framework before adding manual commits: https://frappeframework.com/docs/user/en/api/database#database-transaction-model If you think manual commit is required then add a comment explaining why and `// nosemgrep` on the same line.
-  paths:
-      exclude:
-        - "**/patches/**"
-        - "**/demo/**"
-  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/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/README.md b/README.md
index 6fad8f4..1105a97 100644
--- a/README.md
+++ b/README.md
@@ -55,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
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/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_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/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_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js
index 9003af5..7d8f356 100644
--- a/erpnext/accounts/doctype/pos_settings/pos_settings.js
+++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js
@@ -2,11 +2,11 @@
 // For license information, please see license.txt
 
 let search_fields_datatypes = ['Data', 'Link', 'Dynamic Link', 'Long Text', 'Select', 'Small Text', 'Text', 'Text Editor'];
-let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "hub_sync_id", "asset_naming_series",
+let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "asset_naming_series",
 	"default_material_request_type", "valuation_method", "warranty_period", "weight_uom", "batch_number_series",
 	"serial_no_series", "purchase_uom", "customs_tariff_number", "sales_uom", "deferred_revenue_account",
 	"deferred_expense_account", "quality_inspection_template", "route", "slideshow", "website_image_alt", "thumbnail",
-	"web_long_description", "hub_sync_id"]
+	"web_long_description"]
 
 frappe.ui.form.on('POS Settings', {
 	onload: function(frm) {
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 0637fda..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):
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.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.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index dafae31..9190124 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1427,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:
@@ -1434,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"
@@ -1463,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
@@ -1959,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:
@@ -1990,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/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 c3cb839..508c7af 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:
@@ -165,6 +174,7 @@
 	ldc_name = frappe.db.get_value('Lower Deduction Certificate',
 		{
 			'pan_no': pan_no,
+			'tax_withholding_category': tax_details.tax_withholding_category,
 			'valid_from': ('>=', tax_details.from_date),
 			'valid_upto': ('<=', tax_details.to_date)
 		}, 'name')
diff --git a/erpnext/hub_node/doctype/__init__.py b/erpnext/accounts/print_format_field_template/__init__.py
similarity index 100%
rename from erpnext/hub_node/doctype/__init__.py
rename to erpnext/accounts/print_format_field_template/__init__.py
diff --git a/erpnext/hub/__init__.py b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py
similarity index 100%
copy from erpnext/hub/__init__.py
copy to erpnext/accounts/print_format_field_template/purchase_invoice_taxes/__init__.py
diff --git a/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json
new file mode 100644
index 0000000..f525f7b
--- /dev/null
+++ b/erpnext/accounts/print_format_field_template/purchase_invoice_taxes/purchase_invoice_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:06:53.083133",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Purchase Invoice",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:06:53.083133",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Purchase Invoice Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/hub/__init__.py b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py
similarity index 100%
copy from erpnext/hub/__init__.py
copy to erpnext/accounts/print_format_field_template/sales_invoice_taxes/__init__.py
diff --git a/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json
new file mode 100644
index 0000000..8ce62a8
--- /dev/null
+++ b/erpnext/accounts/print_format_field_template/sales_invoice_taxes/sales_invoice_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 17:50:00.152759",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Sales Invoice",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:13:20.894207",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
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 a600ead..0475231 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -114,8 +114,9 @@
 
 		# 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)
+			if 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
 
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/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/hub/__init__.py b/erpnext/buying/print_format_field_template/__init__.py
similarity index 100%
rename from erpnext/hub/__init__.py
rename to erpnext/buying/print_format_field_template/__init__.py
diff --git a/erpnext/hub/__init__.py b/erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py
similarity index 100%
copy from erpnext/hub/__init__.py
copy to erpnext/buying/print_format_field_template/purchase_order_taxes/__init__.py
diff --git a/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json b/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json
new file mode 100644
index 0000000..73b7730
--- /dev/null
+++ b/erpnext/buying/print_format_field_template/purchase_order_taxes/purchase_order_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:07:19.253457",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Purchase Order",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:07:19.253457",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Purchase Order Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/hub/__init__.py b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py
similarity index 100%
copy from erpnext/hub/__init__.py
copy to erpnext/buying/print_format_field_template/supplier_quotation_taxes/__init__.py
diff --git a/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json
new file mode 100644
index 0000000..2be17a1
--- /dev/null
+++ b/erpnext/buying/print_format_field_template/supplier_quotation_taxes/supplier_quotation_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:09:08.103919",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Supplier Quotation",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:09:08.103919",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Quotation Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e9b531e..37b8f9d 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -815,6 +815,38 @@
 			if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
 				unlink_ref_doc_from_payment_entries(self)
 
+			if self.doctype == "Sales Order":
+				self.unlink_ref_doc_from_po()
+
+	def unlink_ref_doc_from_po(self):
+		so_items = []
+		for item in self.items:
+			so_items.append(item.name)
+
+		linked_po = list(set(frappe.get_all(
+			'Purchase Order Item',
+			filters = {
+				'sales_order': self.name,
+				'sales_order_item': ['in', so_items],
+				'docstatus': ['<', 2]
+			},
+			pluck='parent'
+		)))
+
+		if linked_po:
+			frappe.db.set_value(
+				'Purchase Order Item', {
+					'sales_order': self.name,
+					'sales_order_item': ['in', so_items],
+					'docstatus': ['<', 2]
+				},{
+					'sales_order': None,
+					'sales_order_item': None
+				}
+			)
+
+			frappe.msgprint(_("Purchase Orders {0} are un-linked").format("\n".join(linked_po)))
+
 	def get_tax_map(self):
 		tax_map = {}
 		for tax in self.get('taxes'):
@@ -1032,7 +1064,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 +1386,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 +1403,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 +1719,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/queries.py b/erpnext/controllers/queries.py
index 7b4566a..05ece4d 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -132,7 +132,8 @@
 	return frappe.db.sql("""select {field} from `tabSupplier`
 		where docstatus < 2
 			and ({key} like %(txt)s
-				or supplier_name like %(txt)s) and disabled=0
+			or supplier_name like %(txt)s) and disabled=0
+			and (on_hold = 0 or (on_hold = 1 and CURDATE() > release_date))
 			{mcond}
 		order by
 			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
@@ -565,7 +566,7 @@
 
 		query_filters.append(['name', query_selector, dimensions])
 
-	output = frappe.get_all(doctype, filters=query_filters)
+	output = frappe.get_list(doctype, filters=query_filters)
 	result = [d.name for d in output]
 
 	return [(d,) for d in set(result)]
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/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index be843a3..0e469ac 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'):
@@ -304,6 +314,8 @@
 @frappe.whitelist()
 def make_customer(source_name, target_doc=None):
 	def set_missing_values(source, target):
+		target.opportunity_name = source.name
+
 		if source.opportunity_from == "Lead":
 			target.lead_name = source.party_name
 
diff --git a/erpnext/demo/data/drug_list.json b/erpnext/demo/data/drug_list.json
index e91c30d..3069042 100644
--- a/erpnext/demo/data/drug_list.json
+++ b/erpnext/demo/data/drug_list.json
@@ -60,7 +60,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -144,7 +143,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -226,7 +224,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -308,7 +305,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -390,7 +386,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -472,7 +467,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -554,7 +548,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -636,7 +629,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -718,7 +710,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -800,7 +791,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -882,7 +872,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -964,7 +953,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1046,7 +1034,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1128,7 +1115,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1210,7 +1196,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1292,7 +1277,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1374,7 +1358,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1456,7 +1439,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1538,7 +1520,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1620,7 +1601,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1702,7 +1682,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1784,7 +1763,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1866,7 +1844,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -1948,7 +1925,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2030,7 +2006,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2112,7 +2087,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2194,7 +2168,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2276,7 +2249,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2358,7 +2330,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2440,7 +2411,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2522,7 +2492,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2604,7 +2573,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2686,7 +2654,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2768,7 +2735,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2850,7 +2816,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -2932,7 +2897,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3014,7 +2978,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3098,7 +3061,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3180,7 +3142,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3262,7 +3223,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3344,7 +3304,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3426,7 +3385,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3508,7 +3466,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3590,7 +3547,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3672,7 +3628,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3754,7 +3709,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3836,7 +3790,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -3918,7 +3871,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4000,7 +3952,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4082,7 +4033,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4164,7 +4114,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4246,7 +4195,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4328,7 +4276,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4410,7 +4357,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4492,7 +4438,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4574,7 +4519,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4656,7 +4600,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4738,7 +4681,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4820,7 +4762,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4902,7 +4843,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -4984,7 +4924,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -5066,7 +5005,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
@@ -5148,7 +5086,6 @@
   "standard_rate": 0.0,
   "stock_uom": "Nos",
   "supplier_items": [],
-  "synced_with_hub": 0,
   "taxes": [],
   "thumbnail": null,
   "tolerance": 0.0,
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_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
index 4a1064b..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
@@ -100,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",
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/hub_node/__init__.py b/erpnext/hub_node/__init__.py
deleted file mode 100644
index 6ac3255..0000000
--- a/erpnext/hub_node/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-import frappe
-
-
-@frappe.whitelist()
-def enable_hub():
-	hub_settings = frappe.get_doc('Marketplace Settings')
-	hub_settings.register()
-	frappe.db.commit()
-	return hub_settings
-
-@frappe.whitelist()
-def sync():
-	hub_settings = frappe.get_doc('Marketplace Settings')
-	hub_settings.sync()
diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py
deleted file mode 100644
index 5530491..0000000
--- a/erpnext/hub_node/api.py
+++ /dev/null
@@ -1,233 +0,0 @@
-from __future__ import unicode_literals
-
-import json
-
-import frappe
-from frappe import _
-from frappe.desk.form.load import get_attachments
-from frappe.frappeclient import FrappeClient
-from six import string_types
-
-current_user = frappe.session.user
-
-
-@frappe.whitelist()
-def register_marketplace(company, company_description):
-	validate_registerer()
-
-	settings = frappe.get_single('Marketplace Settings')
-	message = settings.register_seller(company, company_description)
-
-	if message.get('hub_seller_name'):
-		settings.registered = 1
-		settings.hub_seller_name = message.get('hub_seller_name')
-		settings.save()
-
-		settings.add_hub_user(frappe.session.user)
-
-	return { 'ok': 1 }
-
-
-@frappe.whitelist()
-def register_users(user_list):
-	user_list = json.loads(user_list)
-
-	settings = frappe.get_single('Marketplace Settings')
-
-	for user in user_list:
-		settings.add_hub_user(user)
-
-	return user_list
-
-
-def validate_registerer():
-	if current_user == 'Administrator':
-		frappe.throw(_('Please login as another user to register on Marketplace'))
-
-	valid_roles = ['System Manager', 'Item Manager']
-
-	if not frappe.utils.is_subset(valid_roles, frappe.get_roles()):
-		frappe.throw(_('Only users with {0} role can register on Marketplace').format(', '.join(valid_roles)),
-			frappe.PermissionError)
-
-
-@frappe.whitelist()
-def call_hub_method(method, params=None):
-	connection = get_hub_connection()
-
-	if isinstance(params, string_types):
-		params = json.loads(params)
-
-	params.update({
-		'cmd': 'hub.hub.api.' + method
-	})
-
-	response = connection.post_request(params)
-	return response
-
-
-def map_fields(items):
-	field_mappings = get_field_mappings()
-	table_fields = [d.fieldname for d in frappe.get_meta('Item').get_table_fields()]
-
-	hub_seller_name = frappe.db.get_value('Marketplace Settings', 'Marketplace Settings', 'hub_seller_name')
-
-	for item in items:
-		for fieldname in table_fields:
-			item.pop(fieldname, None)
-
-		for mapping in field_mappings:
-			local_fieldname = mapping.get('local_fieldname')
-			remote_fieldname = mapping.get('remote_fieldname')
-
-			value = item.get(local_fieldname)
-			item.pop(local_fieldname, None)
-			item[remote_fieldname] = value
-
-		item['doctype'] = 'Hub Item'
-		item['hub_seller'] = hub_seller_name
-		item.pop('attachments', None)
-
-	return items
-
-
-@frappe.whitelist()
-def get_valid_items(search_value=''):
-	items = frappe.get_list(
-		'Item',
-		fields=["*"],
-		filters={
-			'disabled': 0,
-			'item_name': ['like', '%' + search_value + '%'],
-			'publish_in_hub': 0
-		},
-		order_by="modified desc"
-	)
-
-	valid_items = filter(lambda x: x.image and x.description, items)
-
-	def prepare_item(item):
-		item.source_type = "local"
-		item.attachments = get_attachments('Item', item.item_code)
-		return item
-
-	valid_items = map(prepare_item, valid_items)
-
-	return valid_items
-
-@frappe.whitelist()
-def update_item(ref_doc, data):
-	data = json.loads(data)
-
-	data.update(dict(doctype='Hub Item', name=ref_doc))
-	try:
-		connection = get_hub_connection()
-		connection.update(data)
-	except Exception as e:
-		frappe.log_error(message=e, title='Hub Sync Error')
-
-@frappe.whitelist()
-def publish_selected_items(items_to_publish):
-	items_to_publish = json.loads(items_to_publish)
-	items_to_update = []
-	if not len(items_to_publish):
-		frappe.throw(_('No items to publish'))
-
-	for item in items_to_publish:
-		item_code = item.get('item_code')
-		frappe.db.set_value('Item', item_code, 'publish_in_hub', 1)
-
-		hub_dict = {
-			'doctype': 'Hub Tracked Item',
-			'item_code': item_code,
-			'published': 1,
-			'hub_category': item.get('hub_category'),
-			'image_list': item.get('image_list')
-		}
-		frappe.get_doc(hub_dict).insert(ignore_if_duplicate=True)
-
-	items = map_fields(items_to_publish)
-
-	try:
-		item_sync_preprocess(len(items))
-		convert_relative_image_urls_to_absolute(items)
-
-		# TODO: Publish Progress
-		connection = get_hub_connection()
-		connection.insert_many(items)
-
-		item_sync_postprocess()
-	except Exception as e:
-		frappe.log_error(message=e, title='Hub Sync Error')
-
-@frappe.whitelist()
-def unpublish_item(item_code, hub_item_name):
-	''' Remove item listing from the marketplace '''
-
-	response = call_hub_method('unpublish_item', {
-		'hub_item_name': hub_item_name
-	})
-
-	if response:
-		frappe.db.set_value('Item', item_code, 'publish_in_hub', 0)
-		frappe.delete_doc('Hub Tracked Item', item_code)
-	else:
-		frappe.throw(_('Unable to update remote activity'))
-
-@frappe.whitelist()
-def get_unregistered_users():
-	settings = frappe.get_single('Marketplace Settings')
-	registered_users = [user.user for user in settings.users] + ['Administrator', 'Guest']
-	all_users = [user.name for user in frappe.db.get_all('User', filters={'enabled': 1})]
-	unregistered_users = [user for user in all_users if user not in registered_users]
-	return unregistered_users
-
-
-def item_sync_preprocess(intended_item_publish_count):
-	response = call_hub_method('pre_items_publish', {
-		'intended_item_publish_count': intended_item_publish_count
-	})
-
-	if response:
-		frappe.db.set_value("Marketplace Settings", "Marketplace Settings", "sync_in_progress", 1)
-		return response
-	else:
-		frappe.throw(_('Unable to update remote activity'))
-
-
-def item_sync_postprocess():
-	response = call_hub_method('post_items_publish', {})
-	if response:
-		frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'last_sync_datetime', frappe.utils.now())
-	else:
-		frappe.throw(_('Unable to update remote activity'))
-
-	frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'sync_in_progress', 0)
-
-
-def convert_relative_image_urls_to_absolute(items):
-	from six.moves.urllib.parse import urljoin
-
-	for item in items:
-		file_path = item['image']
-
-		if file_path.startswith('/files/'):
-			item['image'] = urljoin(frappe.utils.get_url(), file_path)
-
-
-def get_hub_connection():
-	settings = frappe.get_single('Marketplace Settings')
-	marketplace_url = settings.marketplace_url
-	hub_user = settings.get_hub_user(frappe.session.user)
-
-	if hub_user:
-		password = hub_user.get_password()
-		hub_connection = FrappeClient(marketplace_url, hub_user.user, password)
-		return hub_connection
-	else:
-		read_only_hub_connection = FrappeClient(marketplace_url)
-		return read_only_hub_connection
-
-
-def get_field_mappings():
-	return []
diff --git a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
deleted file mode 100644
index b1e421d..0000000
--- a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "condition": "{'name': ('=', frappe.db.get_single_value('Hub Settings', 'company'))}",
- "creation": "2017-09-07 11:38:43.169065",
- "docstatus": 0,
- "doctype": "Data Migration Mapping",
- "fields": [
-  {
-   "is_child_table": 0,
-   "local_fieldname": "name",
-   "remote_fieldname": "company_name"
-  },
-  {
-   "is_child_table": 0,
-   "local_fieldname": "country",
-   "remote_fieldname": "country"
-  },
-  {
-   "is_child_table": 0,
-   "local_fieldname": "\"city\"",
-   "remote_fieldname": "seller_city"
-  },
-  {
-   "is_child_table": 0,
-   "local_fieldname": "eval:frappe.local.site",
-   "remote_fieldname": "site_name"
-  },
-  {
-   "is_child_table": 0,
-   "local_fieldname": "eval:frappe.session.user",
-   "remote_fieldname": "user"
-  },
-  {
-   "is_child_table": 0,
-   "local_fieldname": "company_logo",
-   "remote_fieldname": "company_logo"
-  }
- ],
- "idx": 2,
- "local_doctype": "Company",
- "mapping_name": "Company to Hub Company",
- "mapping_type": "Push",
- "migration_id_field": "hub_sync_id",
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "name": "Company to Hub Company",
- "owner": "Administrator",
- "page_length": 10,
- "remote_objectname": "Hub Company",
- "remote_primary_key": "name"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
deleted file mode 100644
index d11abeb..0000000
--- a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "condition": "{'reference_doctype': 'Lead', 'user': frappe.db.get_single_value('Hub Settings', 'user'), 'status': 'Pending'}",
- "creation": "2017-09-20 15:06:40.279930",
- "docstatus": 0,
- "doctype": "Data Migration Mapping",
- "fields": [
-  {
-   "is_child_table": 0,
-   "local_fieldname": "email_id",
-   "remote_fieldname": "email_id"
-  },
-  {
-   "is_child_table": 0,
-   "local_fieldname": "lead_name",
-   "remote_fieldname": "lead_name"
-  }
- ],
- "idx": 0,
- "local_doctype": "Lead",
- "local_primary_key": "email_id",
- "mapping_name": "Hub Message to Lead",
- "mapping_type": "Pull",
- "migration_id_field": "hub_sync_id",
- "modified": "2020-09-18 17:26:09.703215",
- "modified_by": "Administrator",
- "name": "Hub Message to Lead",
- "owner": "Administrator",
- "page_length": 10,
- "remote_objectname": "Hub Message",
- "remote_primary_key": "name"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json b/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
deleted file mode 100644
index bcece69..0000000
--- a/erpnext/hub_node/data_migration_mapping/item_to_hub_item/item_to_hub_item.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "condition": "{\"publish_in_hub\": 1}", 
- "creation": "2017-09-07 13:27:52.726350", 
- "docstatus": 0, 
- "doctype": "Data Migration Mapping", 
- "fields": [
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "item_code", 
-   "remote_fieldname": "item_code"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "item_name", 
-   "remote_fieldname": "item_name"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "eval:frappe.db.get_value('Hub Settings' , 'Hub Settings', 'company_email')", 
-   "remote_fieldname": "hub_seller"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "image", 
-   "remote_fieldname": "image"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "image_list", 
-   "remote_fieldname": "image_list"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "item_group", 
-   "remote_fieldname": "item_group"
-  }, 
-  {
-   "is_child_table": 0, 
-   "local_fieldname": "hub_category", 
-   "remote_fieldname": "hub_category"
-  }
- ], 
- "idx": 1, 
- "local_doctype": "Item", 
- "mapping_name": "Item to Hub Item", 
- "mapping_type": "Push", 
- "migration_id_field": "hub_sync_id", 
- "modified": "2018-08-19 22:20:25.727581", 
- "modified_by": "Administrator", 
- "name": "Item to Hub Item", 
- "owner": "Administrator", 
- "page_length": 10, 
- "remote_objectname": "Hub Item", 
- "remote_primary_key": "item_code"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json b/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
deleted file mode 100644
index e90b1dd..0000000
--- a/erpnext/hub_node/data_migration_plan/hub_sync/hub_sync.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "creation": "2017-09-07 11:39:38.445902", 
- "docstatus": 0, 
- "doctype": "Data Migration Plan", 
- "idx": 1, 
- "mappings": [
-  {
-   "enabled": 1, 
-   "mapping": "Item to Hub Item"
-  }
- ], 
- "modified": "2018-08-19 22:20:25.644602", 
- "modified_by": "Administrator", 
- "module": "Hub Node", 
- "name": "Hub Sync", 
- "owner": "Administrator", 
- "plan_name": "Hub Sync", 
- "postprocess_method": "erpnext.hub_node.api.item_sync_postprocess"
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/__init__.py b/erpnext/hub_node/doctype/hub_tracked_item/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js
deleted file mode 100644
index 660532d..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Hub Tracked Item', {
-	refresh: function(frm) {
-
-	}
-});
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
deleted file mode 100644
index 7d07ba4..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json
+++ /dev/null
@@ -1,210 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:item_code", 
- "beta": 0, 
- "creation": "2018-03-18 09:33:50.267762", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 1
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "hub_category", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Hub Category", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-    "allow_bulk_edit": 0,
-    "allow_in_quick_entry": 0,
-    "allow_on_submit": 0,
-    "bold": 0,
-    "collapsible": 0,
-    "columns": 0,
-    "fieldname": "published",
-    "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": "Published",
-    "length": 0,
-    "no_copy": 0,
-    "permlevel": 0,
-    "precision": "",
-    "print_hide": 0,
-    "print_hide_if_no_value": 0,
-    "read_only": 0,
-    "remember_last_selected_value": 0,
-    "report_hide": 0,
-    "reqd": 0,
-    "search_index": 0,
-    "set_only_once": 0,
-    "translatable": 0,
-    "unique": 0
-   },
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "image_list", 
-   "fieldtype": "Long Text", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Image List", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2019-12-10 11:37:35.951019", 
- "modified_by": "Administrator", 
- "module": "Hub Node", 
- "name": "Hub Tracked Item", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }, 
-  {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Item Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 1
-  }
- ], 
- "quick_entry": 1, 
- "read_only": 1, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py
deleted file mode 100644
index 823c79e..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-from frappe.model.document import Document
-
-
-class HubTrackedItem(Document):
-	pass
diff --git a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py b/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
deleted file mode 100644
index c403f90..0000000
--- a/erpnext/hub_node/doctype/hub_tracked_item/test_hub_tracked_item.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-
-class TestHubTrackedItem(unittest.TestCase):
-	pass
diff --git a/erpnext/hub_node/doctype/hub_user/__init__.py b/erpnext/hub_node/doctype/hub_user/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/hub_user/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.json b/erpnext/hub_node/doctype/hub_user/hub_user.json
deleted file mode 100644
index f51ffb4..0000000
--- a/erpnext/hub_node/doctype/hub_user/hub_user.json
+++ /dev/null
@@ -1,140 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "", 
- "beta": 0, 
- "creation": "2018-08-31 12:36:45.627531", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "user", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "User", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "User", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 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, 
-   "fieldname": "hub_user_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": "Hub User", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "password", 
-   "fieldtype": "Password", 
-   "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": "Hub Password", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2020-09-18 17:26:09.703215", 
- "modified_by": "Administrator", 
- "module": "Hub Node", 
- "name": "Hub User", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.py b/erpnext/hub_node/doctype/hub_user/hub_user.py
deleted file mode 100644
index 1f7c8fc..0000000
--- a/erpnext/hub_node/doctype/hub_user/hub_user.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-from frappe.model.document import Document
-
-
-class HubUser(Document):
-	pass
diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.json b/erpnext/hub_node/doctype/hub_users/hub_users.json
deleted file mode 100644
index d42f3fd..0000000
--- a/erpnext/hub_node/doctype/hub_users/hub_users.json
+++ /dev/null
@@ -1,72 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-03-06 04:38:49.891787", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "user", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "User", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "User", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2020-09-18 17:26:09.703215", 
- "modified_by": "Administrator", 
- "module": "Hub Node", 
- "name": "Hub Users", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.py b/erpnext/hub_node/doctype/hub_users/hub_users.py
deleted file mode 100644
index e08ed68..0000000
--- a/erpnext/hub_node/doctype/hub_users/hub_users.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-from frappe.model.document import Document
-
-
-class HubUsers(Document):
-	pass
diff --git a/erpnext/hub_node/doctype/marketplace_settings/__init__.py b/erpnext/hub_node/doctype/marketplace_settings/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/__init__.py
+++ /dev/null
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js
deleted file mode 100644
index 36da832..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Marketplace Settings', {
-	refresh: function(frm) {
-		$('#toolbar-user .marketplace-link').toggle(!frm.doc.disable_marketplace);
-	},
-});
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
deleted file mode 100644
index e784f68..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json
+++ /dev/null
@@ -1,410 +0,0 @@
-{
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 1, 
- "creation": "2018-08-31 15:54:38.795263", 
- "custom": 0, 
- "description": "", 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
- "fields": [
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "disable_marketplace", 
-   "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": "Disable Marketplace", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:!doc.disable_marketplace", 
-   "fieldname": "marketplace_settings_section", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Marketplace Settings", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "https://hubmarket.org", 
-   "fieldname": "marketplace_url", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Marketplace URL (to hide and update label)", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "registered", 
-   "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": "Registered", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "sync_in_progress", 
-   "fieldtype": "Check", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Sync in Progress", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "company", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Company", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Company", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "hub_seller_name", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Hub Seller Name", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "users", 
-   "fieldtype": "Table", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Users", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Hub User", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "last_sync_datetime", 
-   "fieldtype": "Datetime", 
-   "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": "Last Sync On", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
-  {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "", 
-   "depends_on": "eval:1", 
-   "fieldname": "custom_data", 
-   "fieldtype": "Code", 
-   "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": "Custom Data", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 1, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2020-09-18 17:26:09.703215", 
- "modified_by": "Administrator", 
- "module": "Hub Node", 
- "name": "Marketplace Settings", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [
-  {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 0, 
-   "email": 0, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 0, 
-   "read": 1, 
-   "report": 0, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "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": 0, 
-   "role": "All", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
-   "write": 0
-  }
- ], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
deleted file mode 100644
index 33d23f6..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors and contributors
-# For license information, please see license.txt
-
-from __future__ import unicode_literals
-
-import json
-
-import frappe
-from frappe.frappeclient import FrappeClient
-from frappe.model.document import Document
-from frappe.utils import cint
-
-
-class MarketplaceSettings(Document):
-
-	def register_seller(self, company, company_description):
-
-		country, currency, company_logo = frappe.db.get_value('Company', company,
-			['country', 'default_currency', 'company_logo'])
-
-		company_details = {
-			'company': company,
-			'country': country,
-			'currency': currency,
-			'company_description': company_description,
-			'company_logo': company_logo,
-			'site_name': frappe.utils.get_url()
-		}
-
-		hub_connection = self.get_connection()
-
-		response = hub_connection.post_request({
-			'cmd': 'hub.hub.api.add_hub_seller',
-			'company_details': json.dumps(company_details)
-		})
-
-		return response
-
-
-	def add_hub_user(self, user_email):
-		'''Create a Hub User and User record on hub server
-		and if successfull append it to Hub User table
-		'''
-
-		if not self.registered:
-			return
-
-		hub_connection = self.get_connection()
-
-		first_name, last_name = frappe.db.get_value('User', user_email, ['first_name', 'last_name'])
-
-		hub_user = hub_connection.post_request({
-			'cmd': 'hub.hub.api.add_hub_user',
-			'user_email': user_email,
-			'first_name': first_name,
-			'last_name': last_name,
-			'hub_seller': self.hub_seller_name
-		})
-
-		self.append('users', {
-			'user': hub_user.get('user_email'),
-			'hub_user_name': hub_user.get('hub_user_name'),
-			'password': hub_user.get('password')
-		})
-
-		self.save()
-
-	def get_hub_user(self, user):
-		'''Return the Hub User doc from the `users` table if password is set'''
-
-		filtered_users = list(filter(
-			lambda x: x.user == user and x.password,
-			self.users
-		))
-
-		if filtered_users:
-			return filtered_users[0]
-
-
-	def get_connection(self):
-		return FrappeClient(self.marketplace_url)
-
-
-	def unregister(self):
-		"""Disable the User on hubmarket.org"""
-
-@frappe.whitelist()
-def is_marketplace_enabled():
-	if not hasattr(frappe.local, 'is_marketplace_enabled'):
-		frappe.local.is_marketplace_enabled = cint(frappe.db.get_single_value('Marketplace Settings',
-			'disable_marketplace'))
-
-	return frappe.local.is_marketplace_enabled
diff --git a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py b/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py
deleted file mode 100644
index 7922f45..0000000
--- a/erpnext/hub_node/doctype/marketplace_settings/test_marketplace_settings.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# See license.txt
-from __future__ import unicode_literals
-
-import unittest
-
-
-class TestMarketplaceSettings(unittest.TestCase):
-	pass
diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py
deleted file mode 100644
index 2e4c266..0000000
--- a/erpnext/hub_node/legacy.py
+++ /dev/null
@@ -1,148 +0,0 @@
-from __future__ import unicode_literals
-
-import json
-
-import frappe
-from frappe.contacts.doctype.contact.contact import get_default_contact
-from frappe.frappeclient import FrappeClient
-from frappe.utils import nowdate
-from frappe.utils.nestedset import get_root_of
-
-
-def get_list(doctype, start, limit, fields, filters, order_by):
-	pass
-
-def get_hub_connection():
-	if frappe.db.exists('Data Migration Connector', 'Hub Connector'):
-		hub_connector = frappe.get_doc('Data Migration Connector', 'Hub Connector')
-		hub_connection = hub_connector.get_connection()
-		return hub_connection.connection
-
-	# read-only connection
-	hub_connection = FrappeClient(frappe.conf.hub_url)
-	return hub_connection
-
-def make_opportunity(buyer_name, email_id):
-	buyer_name = "HUB-" + buyer_name
-
-	if not frappe.db.exists('Lead', {'email_id': email_id}):
-		lead = frappe.new_doc("Lead")
-		lead.lead_name = buyer_name
-		lead.email_id = email_id
-		lead.save(ignore_permissions=True)
-
-	o = frappe.new_doc("Opportunity")
-	o.opportunity_from = "Lead"
-	o.lead = frappe.get_all("Lead", filters={"email_id": email_id}, fields = ["name"])[0]["name"]
-	o.save(ignore_permissions=True)
-
-@frappe.whitelist()
-def make_rfq_and_send_opportunity(item, supplier):
-	supplier = make_supplier(supplier)
-	contact = make_contact(supplier)
-	item = make_item(item)
-	rfq = make_rfq(item, supplier, contact)
-	status = send_opportunity(contact)
-
-	return {
-		'rfq': rfq,
-		'hub_document_created': status
-	}
-
-def make_supplier(supplier):
-	# make supplier if not already exists
-	supplier = frappe._dict(json.loads(supplier))
-
-	if not frappe.db.exists('Supplier', {'supplier_name': supplier.supplier_name}):
-		supplier_doc = frappe.get_doc({
-			'doctype': 'Supplier',
-			'supplier_name': supplier.supplier_name,
-			'supplier_group': supplier.supplier_group,
-			'supplier_email': supplier.supplier_email
-		}).insert()
-	else:
-		supplier_doc = frappe.get_doc('Supplier', supplier.supplier_name)
-
-	return supplier_doc
-
-def make_contact(supplier):
-	contact_name = get_default_contact('Supplier', supplier.supplier_name)
-	# make contact if not already exists
-	if not contact_name:
-		contact = frappe.get_doc({
-			'doctype': 'Contact',
-			'first_name': supplier.supplier_name,
-			'is_primary_contact': 1,
-			'links': [
-				{'link_doctype': 'Supplier', 'link_name': supplier.supplier_name}
-			]
-		})
-		contact.add_email(supplier.supplier_email, is_primary=True)
-		contact.insert()
-	else:
-		contact = frappe.get_doc('Contact', contact_name)
-
-	return contact
-
-def make_item(item):
-	# make item if not already exists
-	item = frappe._dict(json.loads(item))
-
-	if not frappe.db.exists('Item', {'item_code': item.item_code}):
-		item_doc = frappe.get_doc({
-			'doctype': 'Item',
-			'item_code': item.item_code,
-			'item_group': item.item_group,
-			'is_item_from_hub': 1
-		}).insert()
-	else:
-		item_doc = frappe.get_doc('Item', item.item_code)
-
-	return item_doc
-
-def make_rfq(item, supplier, contact):
-	# make rfq
-	rfq = frappe.get_doc({
-		'doctype': 'Request for Quotation',
-		'transaction_date': nowdate(),
-		'status': 'Draft',
-		'company': frappe.db.get_single_value('Marketplace Settings', 'company'),
-		'message_for_supplier': 'Please supply the specified items at the best possible rates',
-		'suppliers': [
-			{ 'supplier': supplier.name, 'contact': contact.name }
-		],
-		'items': [
-			{
-				'item_code': item.item_code,
-				'qty': 1,
-				'schedule_date': nowdate(),
-				'warehouse': item.default_warehouse or get_root_of("Warehouse"),
-				'description': item.description,
-				'uom': item.stock_uom
-			}
-		]
-	}).insert()
-
-	rfq.save()
-	rfq.submit()
-	return rfq
-
-def send_opportunity(contact):
-	# Make Hub Message on Hub with lead data
-	doc = {
-		'doctype': 'Lead',
-		'lead_name': frappe.db.get_single_value('Marketplace Settings', 'company'),
-		'email_id': frappe.db.get_single_value('Marketplace Settings', 'user')
-	}
-
-	args = frappe._dict(dict(
-		doctype='Hub Message',
-		reference_doctype='Lead',
-		data=json.dumps(doc),
-		user=contact.email_id
-	))
-
-	connection = get_hub_connection()
-	response = connection.insert('Hub Message', args)
-
-	return response.ok
diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json
index 7e53918..6218707 100644
--- a/erpnext/manufacturing/doctype/bom/bom.json
+++ b/erpnext/manufacturing/doctype/bom/bom.json
@@ -436,7 +436,7 @@
    "description": "Item Image (if not slideshow)",
    "fieldname": "website_image",
    "fieldtype": "Attach Image",
-   "label": "Image"
+   "label": "Website Image"
   },
   {
    "allow_on_submit": 1,
@@ -539,7 +539,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-05-16 12:25:09.081968",
+ "modified": "2021-10-27 14:52:04.500251",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 232e3a0..2cd8f8c 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -307,6 +307,9 @@
 		existing_bom_cost = self.total_cost
 
 		for d in self.get("items"):
+			if not d.item_code:
+				continue
+
 			rate = self.get_rm_rate({
 				"company": self.company,
 				"item_code": d.item_code,
@@ -599,7 +602,7 @@
 		for d in self.get('items'):
 			if d.bom_no:
 				self.get_child_exploded_items(d.bom_no, d.stock_qty)
-			else:
+			elif d.item_code:
 				self.add_to_cur_exploded_items(frappe._dict({
 					'item_code'		: d.item_code,
 					'item_name'		: d.item_name,
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/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 913fc85..7f8e816 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -182,6 +182,7 @@
    "reqd": 1
   },
   {
+   "default": "1.0",
    "fieldname": "qty",
    "fieldtype": "Float",
    "label": "Qty To Manufacture",
@@ -572,10 +573,11 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2021-08-24 15:14:03.844937",
+ "modified": "2021-10-27 19:21:35.139888",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
+ "naming_rule": "By \"Naming Series\" field",
  "nsm_parent_field": "parent_work_order",
  "owner": "Administrator",
  "permissions": [
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index e282dd3..f881e1b 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -685,9 +685,7 @@
 					if not d.operation:
 						d.operation = operation
 			else:
-				# Attribute a big number (999) to idx for sorting putpose in case idx is NULL
-				# For instance in BOM Explosion Item child table, the items coming from sub assembly items
-				for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999):
+				for item in sorted(item_dict.values(), key=lambda d: d['idx'] or float('inf')):
 					self.append('required_items', {
 						'rate': item.rate,
 						'amount': item.rate * item.qty,
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/modules.txt b/erpnext/modules.txt
index a9f94ce..15a24a7 100644
--- a/erpnext/modules.txt
+++ b/erpnext/modules.txt
@@ -20,9 +20,8 @@
 ERPNext Integrations
 Non Profit
 Hotels
-Hub Node
 Quality Management
 Communication
 Loan Management
 Payroll
-Telephony
+Telephony
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index bd16692..2c21ab6 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -58,11 +58,7 @@
 erpnext.patches.v11_0.update_allow_transfer_for_manufacture
 erpnext.patches.v11_0.add_item_group_defaults
 erpnext.patches.v11_0.add_expense_claim_default_account
-execute:frappe.delete_doc("Page", "hub")
-erpnext.patches.v11_0.reset_publish_in_hub_for_all_items
-erpnext.patches.v11_0.update_hub_url # 2018-08-31  # 2018-09-03
 erpnext.patches.v11_0.make_job_card
-erpnext.patches.v10_0.delete_hub_documents # 12-08-2018
 erpnext.patches.v11_0.add_default_dispatch_notification_template
 erpnext.patches.v11_0.add_market_segments
 erpnext.patches.v11_0.add_sales_stages
@@ -153,7 +149,6 @@
 erpnext.patches.v12_0.add_eway_bill_in_delivery_note
 erpnext.patches.v12_0.set_lead_title_field
 erpnext.patches.v12_0.set_permission_einvoicing
-erpnext.patches.v12_0.set_published_in_hub_tracked_item
 erpnext.patches.v12_0.set_job_offer_applicant_email
 erpnext.patches.v12_0.create_irs_1099_field_united_states
 erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
@@ -294,6 +289,7 @@
 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
@@ -307,3 +303,9 @@
 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.update_category_in_ltds_certificate
+erpnext.patches.v13_0.create_pan_field_for_india #2
+erpnext.patches.v14_0.delete_hub_doctypes
diff --git a/erpnext/patches/v10_0/delete_hub_documents.py b/erpnext/patches/v10_0/delete_hub_documents.py
deleted file mode 100644
index 16c7abf..0000000
--- a/erpnext/patches/v10_0/delete_hub_documents.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-
-def execute():
-	for dt, dn in (("Page", "Hub"), ("DocType", "Hub Settings"), ("DocType", "Hub Category")):
-		frappe.delete_doc(dt, dn, ignore_missing=True)
-
-	if frappe.db.exists("DocType", "Data Migration Plan"):
-		data_migration_plans = frappe.get_all("Data Migration Plan", filters={"module": 'Hub Node'})
-		for plan in data_migration_plans:
-			plan_doc = frappe.get_doc("Data Migration Plan", plan.name)
-			for m in plan_doc.get("mappings"):
-				frappe.delete_doc("Data Migration Mapping", m.mapping, force=True)
-			docs = frappe.get_all("Data Migration Run", filters={"data_migration_plan": plan.name})
-			for doc in docs:
-				frappe.delete_doc("Data Migration Run", doc.name)
-			frappe.delete_doc("Data Migration Plan", plan.name)
diff --git a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py b/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
deleted file mode 100644
index a664baf..0000000
--- a/erpnext/patches/v11_0/reset_publish_in_hub_for_all_items.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-
-def execute():
-	frappe.reload_doc('stock', 'doctype', 'item')
-	frappe.db.sql("""update `tabItem` set publish_in_hub = 0""")
diff --git a/erpnext/patches/v11_0/update_hub_url.py b/erpnext/patches/v11_0/update_hub_url.py
deleted file mode 100644
index c89b9b5..0000000
--- a/erpnext/patches/v11_0/update_hub_url.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-
-def execute():
-	frappe.reload_doc('hub_node', 'doctype', 'Marketplace Settings')
-	frappe.db.set_value('Marketplace Settings', 'Marketplace Settings', 'marketplace_url', 'https://hubmarket.org')
diff --git a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
deleted file mode 100644
index 73c6ce8..0000000
--- a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-
-def execute():
-	frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item")
-	if not frappe.db.a_row_exists("Hub Tracked Item"):
-		return
-
-	frappe.db.sql('''
-		Update `tabHub Tracked Item`
-		SET published = 1
-	''')
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/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/update_category_in_ltds_certificate.py b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
new file mode 100644
index 0000000..4d46452
--- /dev/null
+++ b/erpnext/patches/v13_0/update_category_in_ltds_certificate.py
@@ -0,0 +1,18 @@
+import frappe
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	ldc = frappe.qb.DocType("Lower Deduction Certificate").as_("ldc")
+	supplier = frappe.qb.DocType("Supplier")
+
+	frappe.qb.update(ldc).inner_join(supplier).on(
+		ldc.supplier == supplier.name
+	).set(
+		ldc.tax_withholding_category, supplier.tax_withholding_category
+	).where(
+		ldc.tax_withholding_category.isnull()
+	).run()
\ No newline at end of file
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/patches/v14_0/delete_hub_doctypes.py b/erpnext/patches/v14_0/delete_hub_doctypes.py
new file mode 100644
index 0000000..d1e9e31
--- /dev/null
+++ b/erpnext/patches/v14_0/delete_hub_doctypes.py
@@ -0,0 +1,10 @@
+import frappe
+
+
+def execute():
+
+	doctypes = frappe.get_all("DocType", {"module": "Hub Node", "custom": 0}, pluck='name')
+	for doctype in doctypes:
+		frappe.delete_doc("DocType", doctype, ignore_missing=True)
+
+	frappe.delete_doc("Module Def", "Hub Node", 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.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 178cd5c..c4b6a38 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -536,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
@@ -1007,3 +1062,17 @@
 		salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
 
 	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/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/public/build.json b/erpnext/public/build.json
index 6b70dab..f8e8177 100644
--- a/erpnext/public/build.json
+++ b/erpnext/public/build.json
@@ -1,14 +1,10 @@
 {
 	"css/erpnext.css": [
 		"public/less/erpnext.less",
-		"public/less/hub.less",
 		"public/scss/call_popup.scss",
 		"public/scss/point-of-sale.scss",
 		"public/scss/hierarchy_chart.scss"
 	],
-	"css/marketplace.css": [
-		"public/less/hub.less"
-	],
 	"js/erpnext-web.min.js": [
 		"public/js/website_utils.js",
 		"public/js/shopping_cart.js"
@@ -17,9 +13,6 @@
 		"public/scss/website.scss",
 		"public/scss/shopping_cart.scss"
 	],
-	"js/marketplace.min.js": [
-		"public/js/hub/marketplace.js"
-	],
 	"js/erpnext.min.js": [
 		"public/js/conf.js",
 		"public/js/utils.js",
@@ -41,7 +34,6 @@
 		"public/js/utils/supplier_quick_entry.js",
 		"public/js/education/student_button.html",
 		"public/js/education/assessment_result_tool.html",
-		"public/js/hub/hub_factory.js",
 		"public/js/call_popup/call_popup.js",
 		"public/js/utils/dimension_tree_filter.js",
 		"public/js/telephony.js",
diff --git a/erpnext/public/images/hub_logo.svg b/erpnext/public/images/hub_logo.svg
deleted file mode 100644
index 4af4821..0000000
--- a/erpnext/public/images/hub_logo.svg
+++ /dev/null
@@ -1,112 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="330"
-   height="345.43808"
-   viewBox="0 0 87.312496 91.397155"
-   version="1.1"
-   id="svg4635"
-   inkscape:version="0.92.2 5c3e80d, 2017-08-06"
-   sodipodi:docname="hub-logo.svg"
-   inkscape:export-filename="/home/raghu/Desktop/hub-logo.png"
-   inkscape:export-xdpi="95.878258"
-   inkscape:export-ydpi="95.878258">
-  <defs
-     id="defs4629" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.7"
-     inkscape:cx="234.27717"
-     inkscape:cy="167.57445"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:window-width="1920"
-     inkscape:window-height="1149"
-     inkscape:window-x="0"
-     inkscape:window-y="24"
-     inkscape:window-maximized="1" />
-  <metadata
-     id="metadata4632">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-        <cc:license
-           rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
-      </cc:Work>
-      <cc:License
-         rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
-        <cc:permits
-           rdf:resource="http://creativecommons.org/ns#Reproduction" />
-        <cc:permits
-           rdf:resource="http://creativecommons.org/ns#Distribution" />
-        <cc:requires
-           rdf:resource="http://creativecommons.org/ns#Notice" />
-        <cc:requires
-           rdf:resource="http://creativecommons.org/ns#Attribution" />
-        <cc:permits
-           rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
-        <cc:requires
-           rdf:resource="http://creativecommons.org/ns#ShareAlike" />
-      </cc:License>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(121.51931,-138.66452)">
-    <rect
-       rx="13.229166"
-       inkscape:export-ydpi="96"
-       inkscape:export-xdpi="96"
-       inkscape:export-filename="/home/raghu/Desktop/send/hub-02.png"
-       style="opacity:1;vector-effect:none;fill:#89da29;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
-       id="rect828"
-       width="87.3125"
-       height="87.3125"
-       x="-121.51931"
-       y="142.74918"
-       ry="13.229166" />
-    <path
-       style="opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
-       clip-path="none"
-       d="m -121.51931,202.96343 v 13.86892 c 0,7.32897 5.90017,13.22917 13.22916,13.22917 h 60.854162 c 6.610072,0 12.056133,-4.80013 13.061216,-11.1187 -43.339761,0.1608 -54.359752,-16.03276 -87.144538,-15.97939 z"
-       id="path830"
-       inkscape:connector-curvature="0" />
-    <path
-       style="opacity:1;vector-effect:none;fill:#59b81c;fill-opacity:1;stroke:none;stroke-width:3.96875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
-       clip-path="none"
-       d="m -34.20681,202.96343 c -32.784694,-0.0533 -43.804846,16.14019 -87.14455,15.97939 1.00509,6.31857 6.45115,11.1187 13.06122,11.1187 h 60.854164 c 7.328992,0 13.229166,-5.9002 13.229166,-13.22917 z"
-       id="path832"
-       inkscape:connector-curvature="0" />
-    <path
-       id="path834"
-       d="m -84.351263,175.75725 c -1.30945,0 -2.376091,1.06665 -2.376091,2.37608 v 10.02885 0.001 c 0.06583,4.83083 4.01156,8.73477 8.857351,8.73486 4.8718,5e-5 8.846821,-3.94421 8.871295,-8.81134 v -0.001 -9.95288 c 0,-1.30943 -1.066113,-2.37557 -2.375589,-2.37557 -1.309396,0 -2.376064,1.06614 -2.376064,2.37557 v 9.8888 c 0,2.26045 -1.858169,4.10983 -4.119642,4.10983 -2.263616,0 -4.105699,-1.82766 -4.105699,-4.08968 v -9.90844 c 0,-1.30943 -1.066138,-2.37608 -2.375561,-2.37608 z m -20.887107,0.0925 c -1.30943,0 -2.37609,1.06717 -2.37609,2.3766 v 16.45119 c 0,1.30944 1.06666,2.37609 2.37609,2.37609 1.30945,0 2.37556,-1.06665 2.37556,-2.37609 v -5.97327 h 8.22534 v 5.97327 c 0,1.30944 1.066641,2.37609 2.376091,2.37609 1.309423,0 2.375561,-1.06665 2.375561,-2.37609 v -16.45119 c 0,-1.30943 -1.066138,-2.3766 -2.375561,-2.3766 -1.30945,0 -2.376091,1.06717 -2.376091,2.3766 v 5.72627 h -8.22534 v -5.72627 c 0,-1.30943 -1.06611,-2.3766 -2.37556,-2.3766 z m 41.77419,0 c -0.654712,0 -1.248675,0.26711 -1.678967,0.69764 -0.05368,0.0537 -0.105119,0.10983 -0.153458,0.16846 v 5.3e-4 c -0.04839,0.0586 -0.09427,0.11929 -0.136949,0.18242 v 5.3e-4 c -0.256381,0.37936 -0.406691,0.83617 -0.406691,1.32705 v 16.45119 c 0,0.1635 0.01693,0.3242 0.04858,0.47852 0.09512,0.46331 0.32594,0.87828 0.64852,1.20096 0.161369,0.16136 0.345308,0.29938 0.547264,0.40928 v 0 c 0.134567,0.0732 0.276781,0.13403 0.425318,0.18035 v 0 c 0.148537,0.0463 0.303186,0.0783 0.462518,0.0946 v 0 c 0.07959,0.008 0.160708,0.0124 0.242358,0.0124 h 8.33181 c 0.08747,0 0.167931,-0.0145 0.251142,-0.0238 l 0.09509,0.005 c 0.06019,0.003 0.119407,0.005 0.178779,0.006 h 0.0037 0.0048 c 3.578305,-2e-5 6.487954,-2.90916 6.487981,-6.48747 v -0.001 c -0.0026,-1.51334 -0.578009,-2.9475 -1.540484,-4.10673 0.962448,-1.15892 1.537785,-2.59314 1.540484,-4.10621 v -0.001 c -2.7e-5,-3.57831 -2.909676,-6.48744 -6.487981,-6.48746 h -0.533294 z m 8.865103,4.75062 c 0.96393,0 1.736831,0.77394 1.736831,1.73788 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 v 0 h -0.532792 -5.955718 v -3.47317 h 5.956248 z m 0,8.21552 v 0 c 0.963507,5.3e-4 1.735799,0.77373 1.735799,1.73736 0,0.96394 -0.772901,1.73684 -1.736831,1.73684 h -0.0048 l -0.533294,0.0119 h -5.951591 v -3.4742 h 5.959846 z"
-       style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
-       inkscape:connector-curvature="0" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#63c923;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.93750048;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m -77.859375,138.66406 c -9.653316,0 -18.439915,3.93483 -24.767575,10.28125 a 3.9691471,3.9691471 0 1 0 5.621091,5.60352 c 4.899576,-4.9141 11.6422,-7.94727 19.146484,-7.94727 7.501101,0 14.241542,3.03098 19.140625,7.94141 a 3.9691471,3.9691471 0 1 0 5.619141,-5.60547 c -6.327038,-6.34169 -15.110547,-10.27344 -24.759766,-10.27344 z"
-       id="path838"
-       inkscape:connector-curvature="0" />
-  </g>
-</svg>
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/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index febdb24..5259bdc 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -18,7 +18,6 @@
 import "./utils/supplier_quick_entry";
 import "./education/student_button.html";
 import "./education/assessment_result_tool.html";
-import "./hub/hub_factory";
 import "./call_popup/call_popup";
 import "./utils/dimension_tree_filter";
 import "./telephony";
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index 0d79b10..1a309ba 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -113,15 +113,15 @@
 			"fieldname":"period_start_date",
 			"label": __("Start Date"),
 			"fieldtype": "Date",
-			"hidden": 1,
-			"reqd": 1
+			"reqd": 1,
+			"depends_on": "eval:doc.filter_based_on == 'Date Range'"
 		},
 		{
 			"fieldname":"period_end_date",
 			"label": __("End Date"),
 			"fieldtype": "Date",
-			"hidden": 1,
-			"reqd": 1
+			"reqd": 1,
+			"depends_on": "eval:doc.filter_based_on == 'Date Range'"
 		},
 		{
 			"fieldname":"from_fiscal_year",
@@ -129,7 +129,8 @@
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
 			"default": frappe.defaults.get_user_default("fiscal_year"),
-			"reqd": 1
+			"reqd": 1,
+			"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
 		},
 		{
 			"fieldname":"to_fiscal_year",
@@ -137,7 +138,8 @@
 			"fieldtype": "Link",
 			"options": "Fiscal Year",
 			"default": frappe.defaults.get_user_default("fiscal_year"),
-			"reqd": 1
+			"reqd": 1,
+			"depends_on": "eval:doc.filter_based_on == 'Fiscal Year'"
 		},
 		{
 			"fieldname": "periodicity",
diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue
deleted file mode 100644
index 54c3597..0000000
--- a/erpnext/public/js/hub/PageContainer.vue
+++ /dev/null
@@ -1,119 +0,0 @@
-<template>
-	<div class="hub-page-container">
-		<component :is="current_page.component" :key="current_page.key"></component>
-	</div>
-</template>
-
-<script>
-
-import Home from './pages/Home.vue';
-import Search from './pages/Search.vue';
-import Category from './pages/Category.vue';
-import SavedItems from './pages/SavedItems.vue';
-import FeaturedItems from './pages/FeaturedItems.vue';
-import PublishedItems from './pages/PublishedItems.vue';
-import Item from './pages/Item.vue';
-import Seller from './pages/Seller.vue';
-import SellerItems from './pages/SellerItems.vue';
-import Publish from './pages/Publish.vue';
-import Buying from './pages/Buying.vue';
-import Selling from './pages/Selling.vue';
-import Messages from './pages/Messages.vue';
-import NotFound from './pages/NotFound.vue';
-
-function get_route_map() {
-	const read_only_routes = {
-		'marketplace/home': Home,
-		'marketplace/search/:category/:keyword': Search,
-		'marketplace/category/:category': Category,
-		'marketplace/item/:item': Item,
-		'marketplace/seller/:seller': Seller,
-		'marketplace/seller/:seller/items': SellerItems,
-		'marketplace/not-found': NotFound,
-	}
-	const registered_routes = {
-		'marketplace/profile': Seller,
-		'marketplace/saved-items': SavedItems,
-		'marketplace/featured-items': FeaturedItems,
-		'marketplace/publish': Publish,
-		'marketplace/published-items': PublishedItems,
-		'marketplace/buying': Buying,
-		'marketplace/buying/:item': Messages,
-		'marketplace/selling': Selling,
-		'marketplace/selling/:buyer/:item': Messages
-	}
-
-	return hub.is_seller_registered()
-		? Object.assign({}, read_only_routes, registered_routes)
-		: read_only_routes;
-}
-
-export default {
-	data() {
-		return {
-			current_page: this.get_current_page()
-		}
-	},
-	mounted() {
-		frappe.route.on('change', () => {
-			if (frappe.get_route()[0] === 'marketplace') {
-				this.set_current_page();
-				frappe.utils.scroll_to(0);
-			}
-		});
-	},
-	methods: {
-		set_current_page() {
-			this.current_page = this.get_current_page();
-		},
-		get_current_page() {
-			const route_map = get_route_map();
-			const curr_route = frappe.get_route_str();
-			let route = Object.keys(route_map).filter(route => route == curr_route)[0];
-			if (!route) {
-				// find route by matching it with dynamic part
-				const curr_route_parts = curr_route.split('/');
-				const weighted_routes = Object.keys(route_map)
-					.map(route_str => route_str.split('/'))
-					.filter(route_parts => route_parts.length === curr_route_parts.length)
-					.reduce((obj, route_parts) => {
-						const key = route_parts.join('/');
-						let weight = 0;
-						route_parts.forEach((part, i) => {
-							const curr_route_part = curr_route_parts[i];
-							if (part === curr_route_part || part.includes(':')) {
-								weight += 1;
-							}
-						});
-
-						obj[key] = weight;
-						return obj;
-					}, {});
-
-				// get the route with the highest weight
-				for (let key in weighted_routes) {
-					const route_weight = weighted_routes[key];
-					if (route_weight === curr_route_parts.length) {
-						route = key;
-						break;
-					} else {
-						route = null;
-					}
-				}
-			}
-
-			if (!route) {
-				return {
-					key: 'not-found',
-					component: NotFound
-				};
-			}
-
-			return {
-				key: curr_route,
-				component: route_map[route]
-			}
-		}
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/Sidebar.vue b/erpnext/public/js/hub/Sidebar.vue
deleted file mode 100644
index 66c291e..0000000
--- a/erpnext/public/js/hub/Sidebar.vue
+++ /dev/null
@@ -1,110 +0,0 @@
-<template>
-	<div ref="sidebar-container">
-		<ul class="list-unstyled hub-sidebar-group" data-nav-buttons>
-			<li class="hub-sidebar-item" v-for="item in items" :key="item.label" v-route="item.route" v-show="item.condition === undefined || item.condition()">
-				{{ item.label }}
-			</li>
-		</ul>
-		<ul class="list-unstyled hub-sidebar-group" data-categories>
-			<li class="hub-sidebar-item is-title bold text-muted">
-				{{ __('Categories') }}
-			</li>
-			<li class="hub-sidebar-item" v-for="category in categories" :key="category.label" v-route="category.route">
-				{{ category.label }}
-			</li>
-		</ul>
-	</div>
-</template>
-<script>
-export default {
-	data() {
-		return {
-			hub_registered: hub.is_user_registered(),
-			items: [
-				{
-					label: __('Browse'),
-					route: 'marketplace/home'
-				},
-				{
-					label: __('Saved Items'),
-					route: 'marketplace/saved-items',
-					condition: () => this.hub_registered
-				},
-				{
-					label: __('Your Featured Items'),
-					route: 'marketplace/featured-items',
-					condition: () => this.hub_registered
-				},
-				{
-					label: __('Your Profile'),
-					route: 'marketplace/profile',
-					condition: () => this.hub_registered
-				},
-				{
-					label: __('Your Items'),
-					route: 'marketplace/published-items',
-					condition: () => this.hub_registered
-				},
-				{
-					label: __('Publish Items'),
-					route: 'marketplace/publish',
-					condition: () => this.hub_registered
-				},
-				{
-					label: __('Selling'),
-					route: 'marketplace/selling',
-					condition: () => this.hub_registered
-				},
-				{
-					label: __('Buying'),
-					route: 'marketplace/buying',
-					condition: () => this.hub_registered
-				},
-			],
-			categories: []
-		}
-	},
-	created() {
-		this.get_categories()
-			.then(categories => {
-				this.categories = categories.map(c => {
-					return {
-						label: __(c.name),
-						route: 'marketplace/category/' + c.name
-					}
-				});
-				this.categories.unshift({
-					label: __('All'),
-					route: 'marketplace/home'
-				});
-				this.$nextTick(() => {
-					this.update_sidebar_state();
-				});
-			});
-
-		erpnext.hub.on('seller-registered', () => {
-			this.hub_registered = true;
-		})
-	},
-	mounted() {
-		this.update_sidebar_state();
-		frappe.route.on('change', () => this.update_sidebar_state());
-	},
-	methods: {
-		get_categories() {
-			return hub.call('get_categories');
-		},
-		update_sidebar_state() {
-			const container = $(this.$refs['sidebar-container']);
-			const route = frappe.get_route();
-			const route_str = route.join('/');
-			const part_route_str = route.slice(0, 2).join('/');
-			const $sidebar_item = container.find(`[data-route="${route_str}"], [data-route="${part_route_str}"]`);
-
-			const $siblings = container.find('[data-route]');
-			$siblings.removeClass('active').addClass('text-muted');
-			$sidebar_item.addClass('active').removeClass('text-muted');
-		},
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/components/CommentInput.vue b/erpnext/public/js/hub/components/CommentInput.vue
deleted file mode 100644
index 31562c7..0000000
--- a/erpnext/public/js/hub/components/CommentInput.vue
+++ /dev/null
@@ -1,39 +0,0 @@
-<template>
-	<div>
-		<div ref="comment-input"></div>
-		<div class="level">
-			<div class="level-left">
-				<span class="text-muted">{{ __('Ctrl + Enter to submit') }}</span>
-			</div>
-			<div class="level-right">
-				<button class="btn btn-primary btn-xs" @click="submit_input">{{ __('Submit') }}</button>
-			</div>
-		</div>
-	</div>
-</template>
-<script>
-export default {
-	mounted() {
-		this.make_input();
-	},
-	methods: {
-		make_input() {
-			this.message_input = frappe.ui.form.make_control({
-				parent: this.$refs['comment-input'],
-				on_submit: (message) => {
-					this.message_input.reset();
-					this.$emit('change', message);
-				},
-				only_input: true,
-				no_wrapper: true
-			});
-		},
-		submit_input() {
-			if (!this.message_input) return;
-			const value = this.message_input.get_value();
-			if (!value) return;
-			this.message_input.submit();
-		}
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/components/DetailHeaderItem.vue b/erpnext/public/js/hub/components/DetailHeaderItem.vue
deleted file mode 100644
index a6c5f06..0000000
--- a/erpnext/public/js/hub/components/DetailHeaderItem.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<template>
-	<p class="text-muted" v-if="!Array.isArray(this.header_items)" v-html="header_items"></p>
-	<p class="text-muted" v-else>
-		<span v-for="(header_item , index) in header_items" :key="index">
-			<span v-if="index" v-html="spacer"></span>
-			<span v-if="typeof(header_item) == 'string'" v-html="header_item"></span>
-			<a v-else-if="typeof(header_item) == 'object'" @click="header_item.on_click(header_item.value)" v-html="header_item.value"></a>
-		</span>
-	</p>
-</template>
-
-<script>
-
-const spacer = '<span aria-hidden="true"> · </span>';
-
-export default {
-	name: 'detail-header-item',
-	props: ['value'],
-	data() {
-		return {
-			header_items: this.value,
-			spacer: spacer
-		}
-	},
-}
-</script>
diff --git a/erpnext/public/js/hub/components/DetailView.vue b/erpnext/public/js/hub/components/DetailView.vue
deleted file mode 100644
index 942c1eb..0000000
--- a/erpnext/public/js/hub/components/DetailView.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-<template>
-	<div class="hub-item-container">
-		<div class="row visible-xs">
-			<div class="col-xs-12 margin-bottom">
-				<button class="btn btn-xs btn-default" data-route="marketplace/home">{{ back_to_home_text }}</button>
-			</div>
-		</div>
-
-		<div v-if="show_skeleton" class="row margin-bottom">
-			<div class="col-md-3">
-				<div class="hub-item-skeleton-image"></div>
-			</div>
-			<div class="col-md-6">
-				<h2 class="hub-skeleton" style="width: 75%;">Name</h2>
-				<div class="text-muted">
-					<p class="hub-skeleton" style="width: 35%;">Details</p>
-					<p class="hub-skeleton" style="width: 50%;">Ratings</p>
-				</div>
-				<hr>
-				<div class="hub-item-description">
-					<p class="hub-skeleton">Desc</p>
-					<p class="hub-skeleton" style="width: 85%;">Desc</p>
-				</div>
-			</div>
-		</div>
-
-		<div v-else>
-			<div class="row margin-bottom">
-				<div class="col-md-3">
-					<div class="hub-item-image">
-						<base-image :src="image" :alt="title" />
-					</div>
-				</div>
-				<div class="col-md-8" style='padding-left: 30px;'>
-					<h2>{{ title }}</h2>
-					<div class="text-muted">
-						<slot name="detail-header-item"></slot>
-					</div>
-				</div>
-
-				<div v-if="menu_items && menu_items.length" class="col-md-1">
-					<div class="dropdown pull-right hub-item-dropdown">
-						<a class="dropdown-toggle btn btn-xs btn-default" data-toggle="dropdown">
-							<span class="caret"></span>
-						</a>
-						<ul class="dropdown-menu dropdown-right" role="menu">
-							<li v-for="menu_item in menu_items"
-								v-if="menu_item.condition"
-								:key="menu_item.label"
-							>
-								<a @click="menu_item.action">{{ menu_item.label }}</a>
-							</li>
-						</ul>
-					</div>
-				</div>
-			</div>
-			<div v-for="section in sections" class="row hub-item-description margin-bottom"
-				:key="section.title"
-			>
-				<h6 class="col-md-12 margin-top">
-					<b class="text-muted">{{ section.title }}</b>
-				</h6>
-				<p class="col-md-12" v-html="section.content">
-				</p>
-			</div>
-		</div>
-
-	</div>
-</template>
-
-<script>
-
-export default {
-	name: 'detail-view',
-	props: ['title', 'image', 'sections', 'show_skeleton', 'menu_items'],
-	data() {
-		return {
-			back_to_home_text: __('Back to Home')
-		}
-	},
-	computed: {}
-}
-</script>
-
-<style lang="less" scoped>
-</style>
diff --git a/erpnext/public/js/hub/components/EmptyState.vue b/erpnext/public/js/hub/components/EmptyState.vue
deleted file mode 100644
index e3a33a0..0000000
--- a/erpnext/public/js/hub/components/EmptyState.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-<template>
-	<div class="empty-state flex flex-column"
-		:class="{ 'bordered': bordered, 'align-center': centered, 'justify-center': centered }"
-		:style="{ height: height + 'px' }"
-	>
-		<p class="text-muted" v-html="message" ></p>
-		<p v-if="action">
-			<button class="btn btn-default btn-xs"
-				@click="action.on_click"
-			>
-				{{ action.label }}
-			</button>
-		</p>
-	</div>
-</template>
-
-<script>
-
-export default {
-	name: 'empty-state',
-	props: {
-		message: String,
-		bordered: Boolean,
-		height: Number,
-		action: Object,
-		centered: {
-			type: Boolean,
-			default: true
-		}
-	}
-}
-</script>
-
-<style lang="less">
-	@import "../../../../../../frappe/frappe/public/less/variables.less";
-
-	.empty-state {
-		height: 500px;
-	}
-
-	.empty-state.bordered {
-		border-radius: 4px;
-		border: 1px solid @border-color;
-		border-style: dashed;
-
-		// bad, due to item card column layout, that is inner 15px margin
-		margin: 0 15px;
-	}
-
-</style>
diff --git a/erpnext/public/js/hub/components/Image.vue b/erpnext/public/js/hub/components/Image.vue
deleted file mode 100644
index 9acf421..0000000
--- a/erpnext/public/js/hub/components/Image.vue
+++ /dev/null
@@ -1,40 +0,0 @@
-<template>
-	<div class="hub-image">
-		<img :src="src" :alt="alt" v-show="!is_loading && !is_broken"/>
-		<div class="hub-image-loading" v-if="is_loading">
-			<span class="octicon octicon-cloud-download"></span>
-		</div>
-		<div class="hub-image-broken" v-if="is_broken">
-			<span class="octicon octicon-file-media"></span>
-		</div>
-	</div>
-</template>
-<script>
-export default {
-	name: 'Image',
-	props: ['src', 'alt'],
-	data() {
-		return {
-			is_loading: true,
-			is_broken: false
-		}
-	},
-	created() {
-		this.handle_image();
-	},
-	methods: {
-		handle_image() {
-			let img = new Image();
-			img.src = this.src;
-
-			img.onload = () => {
-				this.is_loading = false;
-			};
-			img.onerror = () => {
-				this.is_loading = false;
-				this.is_broken = true;
-			};
-		}
-	}
-};
-</script>
diff --git a/erpnext/public/js/hub/components/ItemCard.vue b/erpnext/public/js/hub/components/ItemCard.vue
deleted file mode 100644
index 675ad86..0000000
--- a/erpnext/public/js/hub/components/ItemCard.vue
+++ /dev/null
@@ -1,142 +0,0 @@
-<template>
-	<div v-if="seen" class="col-md-3 col-sm-4 col-xs-6 hub-card-container">
-		<div class="hub-card"
-			@click="on_click(item_id)"
-		>
-			<div class="hub-card-header flex justify-between">
-				<div class="ellipsis" :style="{ width: '85%' }">
-					<div class="hub-card-title ellipsis bold">{{ title }}</div>
-					<div class="hub-card-subtitle ellipsis text-muted" v-html='subtitle'></div>
-				</div>
-				<i v-if="allow_clear"
-					class="octicon octicon-x text-extra-muted"
-					@click.stop="$emit('remove-item', item_id)"
-				>
-				</i>
-			</div>
-			<div class="hub-card-body">
-				<base-image class="hub-card-image" :src="item.image" :alt="title" />
-				<div class="hub-card-overlay">
-					<div v-if="is_local" class="hub-card-overlay-body">
-						<div class="hub-card-overlay-button">
-							<button class="btn btn-default zoom-view">
-								<i class="octicon octicon-pencil text-muted"></i>
-							</button>
-						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script>
-
-export default {
-	name: 'item-card',
-	props: ['item', 'item_id_fieldname', 'is_local', 'on_click', 'allow_clear', 'seen'],
-	computed: {
-		title() {
-			const item_name = this.item.item_name || this.item.name;
-			return strip_html(item_name);
-		},
-		subtitle() {
-			const dot_spacer = '<span aria-hidden="true"> · </span>';
-			if(this.is_local){
-				return comment_when(this.item.creation);
-			} else {
-				let subtitle_items = [comment_when(this.item.creation)];
-				const rating = this.item.average_rating;
-
-				if (rating > 0) {
-					subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`)
-				}
-
-				subtitle_items.push(this.item.company);
-
-				return subtitle_items.join(dot_spacer);
-			}
-		},
-		item_id() {
-			return this.item[this.item_id_fieldname];
-		}
-	}
-}
-</script>
-
-<style lang="less" scoped>
-	@import "../../../../../../frappe/frappe/public/less/variables.less";
-
-	.hub-card {
-		margin-bottom: 25px;
-		position: relative;
-		border: 1px solid @border-color;
-		border-radius: 4px;
-		overflow: hidden;
-		cursor: pointer;
-
-		&:hover .hub-card-overlay {
-			display: block;
-		}
-
-		.octicon-x {
-			display: block;
-			font-size: 20px;
-			margin-left: 10px;
-			cursor: pointer;
-		}
-	}
-
-	.hub-card.closable {
-		.octicon-x {
-			display: block;
-		}
-	}
-
-	.hub-card.is-local {
-		&.active {
-			.hub-card-header {
-				background-color: #f4ffe5;
-			}
-		}
-	}
-
-	.hub-card-header {
-		position: relative;
-		padding: 12px 15px;
-		height: 60px;
-		border-bottom: 1px solid @border-color;
-	}
-
-	.hub-card-body {
-		position: relative;
-		height: 200px;
-	}
-
-	.hub-card-overlay {
-		display: none;
-		position: absolute;
-		top: 0;
-		width: 100%;
-		height: 100%;
-		background-color: rgba(0, 0, 0, 0.05);
-	}
-
-	.hub-card-overlay-body {
-		position: relative;
-		height: 100%;
-	}
-
-	.hub-card-overlay-button {
-		position: absolute;
-		right: 15px;
-		bottom: 15px;
-	}
-
-	.hub-card-image {
-		width: 100%;
-		height: 100%;
-		object-fit: contain;
-	}
-
-</style>
diff --git a/erpnext/public/js/hub/components/ItemCardsContainer.vue b/erpnext/public/js/hub/components/ItemCardsContainer.vue
deleted file mode 100644
index 0a20bcd..0000000
--- a/erpnext/public/js/hub/components/ItemCardsContainer.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<template>
-	<div class="item-cards-container">
-		<empty-state
-			v-if="items.length === 0"
-			:message="empty_state_message"
-			:action="empty_state_action"
-			:bordered="true"
-			:height="empty_state_height"
-		/>
-		<item-card
-			v-for="item in items"
-			:key="container_name + '_' +item[item_id_fieldname]"
-			:item="item"
-			:item_id_fieldname="item_id_fieldname"
-			:is_local="is_local"
-			:on_click="on_click"
-			:allow_clear="editable"
-			:seen="item.hasOwnProperty('seen') ? item.seen : true"
-			@remove-item="$emit('remove-item', item[item_id_fieldname])"
-		>
-		</item-card>
-	</div>
-</template>
-
-<script>
-import ItemCard from './ItemCard.vue';
-import EmptyState from './EmptyState.vue';
-
-export default {
-	name: 'item-cards-container',
-	props: {
-		container_name: String,
-		items: Array,
-		item_id_fieldname: String,
-		is_local: Boolean,
-		on_click: Function,
-		editable: Boolean,
-
-		empty_state_message: String,
-		empty_state_action: Object,
-		empty_state_height: Number,
-		empty_state_bordered: Boolean
-	},
-	components: {
-		ItemCard,
-		EmptyState
-	},
-	watch: {
-		items() {
-			// TODO: handling doesn't work
-			frappe.dom.handle_broken_images($(this.$el));
-		}
-	}
-}
-</script>
-
-<style scoped>
-	.item-cards-container {
-		margin: 0 -15px;
-		overflow: overlay;
-	}
-</style>
diff --git a/erpnext/public/js/hub/components/ItemListCard.vue b/erpnext/public/js/hub/components/ItemListCard.vue
deleted file mode 100644
index 7f6fb77..0000000
--- a/erpnext/public/js/hub/components/ItemListCard.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-<template>
-	<div class="hub-list-item" :data-route="item.route">
-		<div class="hub-list-left">
-			<base-image class="hub-list-image" :src="item.image" />
-			<div class="hub-list-body ellipsis">
-				<div class="hub-list-title">{{item.item_name}}</div>
-				<div class="hub-list-subtitle ellipsis">
-					<slot name="subtitle"></slot>
-				</div>
-			</div>
-		</div>
-		<div class="hub-list-right" v-if="message">
-			<span class="text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" />
-		</div>
-	</div>
-</template>
-<script>
-export default {
-	props: ['item', 'message']
-}
-</script>
diff --git a/erpnext/public/js/hub/components/NotificationMessage.vue b/erpnext/public/js/hub/components/NotificationMessage.vue
deleted file mode 100644
index c266726..0000000
--- a/erpnext/public/js/hub/components/NotificationMessage.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-<template>
-	<div v-if="message" class="subpage-message">
-        <p class="text-muted flex">
-            <span v-html="message"></span>
-            <i class="octicon octicon-x text-extra-muted"
-                @click="$emit('remove-message')"
-            >
-            </i>
-        </p>
-    </div>
-</template>
-
-<script>
-
-export default {
-	name: 'notification-message',
-	props: {
-		message: String,
-    }
-}
-</script>
-
-<style lang="less" scoped>
-    .subpage-message {
-		p {
-			padding: 10px 15px;
-			margin-top: 0px;
-			margin-bottom: 15px;
-			background-color: #f9fbf7;
-			border-radius: 4px;
-			justify-content: space-between;
-		}
-
-		.octicon-x {
-			cursor: pointer;
-		}
-	}
-</style>
diff --git a/erpnext/public/js/hub/components/Rating.vue b/erpnext/public/js/hub/components/Rating.vue
deleted file mode 100644
index 33290b8..0000000
--- a/erpnext/public/js/hub/components/Rating.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-<template>
-    <span>
-        <i v-for="index in max_rating"
-            :key="index"
-            class="fa fa-fw star-icon"
-            :class="{'fa-star': index <= rating, 'fa-star-o': index > rating}"
-        >
-        </i>
-    </span>
-</template>
-
-<script>
-export default {
-    props: ['rating', 'max_rating']
-}
-</script>
diff --git a/erpnext/public/js/hub/components/ReviewArea.vue b/erpnext/public/js/hub/components/ReviewArea.vue
deleted file mode 100644
index aa83bb0..0000000
--- a/erpnext/public/js/hub/components/ReviewArea.vue
+++ /dev/null
@@ -1,140 +0,0 @@
-<template>
-	<div>
-		<div class="timeline-head">
-			<div class="comment-input-wrapper">
-				<div class="comment-input-header">
-					<span class="text-muted">{{ __('Add your review') }}</span>
-					<div class="btn btn-default btn-xs pull-right"
-						@click="on_submit_review"
-						:disabled="!(user_review.rating && user_review.subject)"
-					>
-						{{ __('Submit Review') }}
-					</div>
-				</div>
-				<div class="comment-input-container">
-					<div class="rating-area text-muted">
-						<span>{{ __('Your rating:') }}</span>
-						<div
-							v-for="i in [1, 2, 3, 4, 5]"
-							:key="i"
-							:class="['fa fa-fw', user_review.rating < i ? 'fa-star-o' : 'fa-star']"
-							:data-index="i"
-							@click="set_rating(i)"
-						>
-						</div>
-					</div>
-					<div class="comment-input-body margin-top" v-show="user_review.rating">
-						<input
-							type="text"
-							placeholder="Subject"
-							class="form-control margin-bottom"
-							style="border-color: #ebeff2"
-							v-model="user_review.subject"
-						>
-						<div ref="review-content"></div>
-						<div>
-							<span class="text-muted text-small">{{ __('Ctrl+Enter to submit') }}</span>
-						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-		<div class="timeline-items">
-			<review-timeline-item v-for="review in reviews"
-				:key="review.user"
-				:username="review.username"
-				:avatar="review.user_image"
-				:comment_when="when(review.modified)"
-				:rating="review.rating"
-				:subject="review.subject"
-				:content="review.content"
-			>
-			</review-timeline-item>
-		</div>
-	</div>
-</template>
-<script>
-import ReviewTimelineItem from '../components/ReviewTimelineItem.vue';
-
-export default {
-	props: ['hub_item_name'],
-	data() {
-		return {
-			user_review: {
-				rating: 0,
-				subject: '',
-				content: ''
-			},
-			reviews: []
-		}
-	},
-	components: {
-		ReviewTimelineItem
-	},
-	created() {
-		this.get_item_reviews();
-	},
-	mounted() {
-		this.make_input();
-	},
-	methods: {
-		set_rating(i) {
-			this.user_review.rating = i;
-		},
-
-		when(datetime) {
-			return comment_when(datetime);
-		},
-
-		get_item_reviews() {
-			hub.call('get_item_reviews', { hub_item_name: this.hub_item_name })
-				.then(reviews => {
-					this.reviews = reviews;
-				})
-				.catch(() => {});
-		},
-
-		make_input() {
-			this.review_content = frappe.ui.form.make_control({
-				parent: this.$refs['review-content'],
-				on_submit: this.on_submit_review.bind(this),
-				no_wrapper: true,
-				only_input: true,
-				render_input: true,
-				df: {
-					fieldtype: 'Comment',
-					fieldname: 'comment'
-				}
-			});
-		},
-
-		on_submit_review() {
-			const review = Object.assign({}, this.user_review, {
-				content: this.review_content.get_value()
-			});
-
-			if (!hub.is_seller_registered()) {
-				frappe.throw(__('You need to login as a Marketplace User before you can add any reviews.'));
-			}
-
-			hub.call('add_item_review', {
-				hub_item_name: this.hub_item_name,
-				review: JSON.stringify(review)
-			})
-			.then(this.push_review.bind(this));
-
-			this.reset_user_review();
-		},
-
-		reset_user_review() {
-			this.user_review.rating = 0;
-			this.user_review.subject = '';
-			this.review_content.set_value('');
-		},
-
-		push_review(review){
-			this.reviews.unshift(review);
-		}
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/components/ReviewTimelineItem.vue b/erpnext/public/js/hub/components/ReviewTimelineItem.vue
deleted file mode 100644
index d0e83f3..0000000
--- a/erpnext/public/js/hub/components/ReviewTimelineItem.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<template>
-    <div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
-		<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
-			<!-- ${image_html} -->
-		</span>
-		<div class="pull-left media-body">
-			<div class="media-content-wrapper">
-				<div class="action-btns">
-                    <!-- ${edit_html} -->
-                </div>
-
-				<div class="comment-header clearfix">
-					<span class="pull-left avatar avatar-small visible-xs">
-						<!-- ${image_html} -->
-					</span>
-
-					<div class="asset-details">
-						<span class="author-wrap">
-							<i class="octicon octicon-quote hidden-xs fa-fw"></i>
-							<span>
-                                {{ username }}
-                            </span>
-						</span>
-						<a class="text-muted">
-							<span class="text-muted hidden-xs">&ndash;</span>
-							<span class="hidden-xs" v-html="comment_when"></span>
-						</a>
-					</div>
-				</div>
-				<div class="reply timeline-content-show">
-					<div class="timeline-item-content">
-						<p class="text-muted">
-							<rating :rating="rating" :max_rating="5"></rating>
-						</p>
-						<h6 class="bold">{{ subject }}</h6>
-						<p class="text-muted" v-html="content"></p>
-					</div>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script>
-import Rating from '../components/Rating.vue';
-
-export default {
-    props: ['username', 'comment_when', 'avatar', 'rating', 'subject', 'content'],
-    components: {
-        Rating
-    }
-}
-</script>
diff --git a/erpnext/public/js/hub/components/SearchInput.vue b/erpnext/public/js/hub/components/SearchInput.vue
deleted file mode 100644
index 4b1ce6e..0000000
--- a/erpnext/public/js/hub/components/SearchInput.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<template>
-  <div class="hub-search-container">
-	<input
-		type="text"
-		class="form-control"
-		:placeholder="placeholder"
-		:value="value"
-		@keydown.enter="on_input">
-  </div>
-</template>
-
-<script>
-export default {
-	props: {
-		placeholder: String,
-		value: String,
-		on_search: Function
-	},
-	methods: {
-		on_input(event) {
-			this.$emit('input', event.target.value);
-			this.on_search();
-		}
-	}
-};
-</script>
diff --git a/erpnext/public/js/hub/components/SectionHeader.vue b/erpnext/public/js/hub/components/SectionHeader.vue
deleted file mode 100644
index 05a2f83..0000000
--- a/erpnext/public/js/hub/components/SectionHeader.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-<template>
-    <div class="hub-items-header level"><slot></slot></div>
-</template>
diff --git a/erpnext/public/js/hub/components/TimelineItem.vue b/erpnext/public/js/hub/components/TimelineItem.vue
deleted file mode 100644
index d13c842..0000000
--- a/erpnext/public/js/hub/components/TimelineItem.vue
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Saving this for later */
-<template>
-	<div class="media timeline-item  notification-content">
-		<div class="small">
-			<i class="octicon octicon-bookmark fa-fw"></i>
-			<span title="Administrator"><b>4 weeks ago</b> Published 1 item to Marketplace</span>
-		</div>
-	</div>
-</template>
diff --git a/erpnext/public/js/hub/components/edit_details_dialog.js b/erpnext/public/js/hub/components/edit_details_dialog.js
deleted file mode 100644
index 97c5f83..0000000
--- a/erpnext/public/js/hub/components/edit_details_dialog.js
+++ /dev/null
@@ -1,41 +0,0 @@
-function edit_details_dialog(params) {
-	let dialog = new frappe.ui.Dialog({
-		title: __('Update Details'),
-		fields: [
-			{
-				label: 'Item Name',
-				fieldname: 'item_name',
-				fieldtype: 'Data',
-				default: params.defaults.item_name,
-				reqd: 1
-			},
-			{
-				label: 'Hub Category',
-				fieldname: 'hub_category',
-				fieldtype: 'Autocomplete',
-				default: params.defaults.hub_category,
-				options: [],
-				reqd: 1
-			},
-			{
-				label: 'Description',
-				fieldname: 'description',
-				fieldtype: 'Text',
-				default: params.defaults.description,
-				options: [],
-				reqd: 1
-			}
-		],
-		primary_action_label: params.primary_action.label || __('Update Details'),
-		primary_action: params.primary_action.fn
-	});
-
-	hub.call('get_categories').then(categories => {
-		categories = categories.map(d => d.name);
-		dialog.fields_dict.hub_category.set_data(categories);
-	});
-
-	return dialog;
-}
-
-export { edit_details_dialog };
diff --git a/erpnext/public/js/hub/components/item_publish_dialog.js b/erpnext/public/js/hub/components/item_publish_dialog.js
deleted file mode 100644
index 08de5b3..0000000
--- a/erpnext/public/js/hub/components/item_publish_dialog.js
+++ /dev/null
@@ -1,39 +0,0 @@
-function ItemPublishDialog(primary_action, secondary_action) {
-	let dialog = new frappe.ui.Dialog({
-		title: __('Edit Publishing Details'),
-		fields: [
-			{
-				label: __('Item Code'),
-				fieldname: 'item_code',
-				fieldtype: 'Data',
-				read_only: 1
-			},
-			{
-				label: __('Hub Category'),
-				fieldname: 'hub_category',
-				fieldtype: 'Autocomplete',
-				options: [],
-				reqd: 1
-			},
-			{
-				label: __('Images'),
-				fieldname: 'image_list',
-				fieldtype: 'MultiSelect',
-				options: [],
-				reqd: 1
-			}
-		],
-		primary_action_label: primary_action.label || __('Set Details'),
-		primary_action: primary_action.fn,
-		secondary_action: secondary_action.fn
-	});
-
-	hub.call('get_categories').then(categories => {
-		categories = categories.map(d => d.name);
-		dialog.fields_dict.hub_category.set_data(categories);
-	});
-
-	return dialog;
-}
-
-export { ItemPublishDialog };
diff --git a/erpnext/public/js/hub/components/profile_dialog.js b/erpnext/public/js/hub/components/profile_dialog.js
deleted file mode 100644
index 8e3abc3..0000000
--- a/erpnext/public/js/hub/components/profile_dialog.js
+++ /dev/null
@@ -1,56 +0,0 @@
-const ProfileDialog = (title = __('Edit Profile'), action={}) => {
-	const fields = [
-		{
-			fieldtype: 'Link',
-			fieldname: 'company',
-			label: __('Company'),
-			options: 'Company'
-		},
-		{
-			fieldtype: 'Read Only',
-			fieldname: 'email',
-			label: __('Email')
-		},
-		{
-			label: __('About your company'),
-			fieldname: 'company_description',
-			fieldtype: 'Text'
-		}
-	];
-
-	let dialog = new frappe.ui.Dialog({
-		title: title,
-		fields: fields,
-		primary_action_label: action.label || __('Update'),
-		primary_action: () => {
-			const form_values = dialog.get_values();
-			let values_filled = true;
-
-			// TODO: Say "we notice that the company description and logo isn't set. Please set them in master."
-			// Only then allow to register
-
-			const mandatory_fields = ['company', 'company_description'];
-			mandatory_fields.forEach(field => {
-				const value = form_values[field];
-				if (!value) {
-					dialog.set_df_property(field, 'reqd', 1);
-					values_filled = false;
-				}
-			});
-			if (!values_filled) return;
-
-			action.on_submit(form_values);
-		}
-	});
-
-	// Post create
-	const default_company = frappe.defaults.get_default('company');
-	dialog.set_value('company', default_company);
-	dialog.set_value('email', frappe.session.user);
-
-	return dialog;
-}
-
-export {
-	ProfileDialog
-}
diff --git a/erpnext/public/js/hub/components/reviews.js b/erpnext/public/js/hub/components/reviews.js
deleted file mode 100644
index e34d680..0000000
--- a/erpnext/public/js/hub/components/reviews.js
+++ /dev/null
@@ -1,80 +0,0 @@
-function get_review_html(review) {
-	let username = review.username || review.user || __("Anonymous");
-
-	let image_html = review.user_image
-		? `<div class="avatar-frame" style="background-image: url(${review.user_image})"></div>`
-		: `<div class="standard-image" style="background-color: #fafbfc">${frappe.get_abbr(username)}</div>`
-
-	let edit_html = review.own
-		? `<div class="pull-right hidden-xs close-btn-container">
-			<span class="small text-muted">
-				${'data.delete'}
-			</span>
-		</div>
-		<div class="pull-right edit-btn-container">
-			<span class="small text-muted">
-				${'data.edit'}
-			</span>
-		</div>`
-		: '';
-
-	let rating_html = get_rating_html(review.rating);
-
-	return get_timeline_item(review, image_html, edit_html, rating_html);
-}
-
-function get_timeline_item(data, image_html, edit_html, rating_html) {
-	return `<div class="media timeline-item user-content" data-doctype="${''}" data-name="${''}">
-		<span class="pull-left avatar avatar-medium hidden-xs" style="margin-top: 1px">
-			${image_html}
-		</span>
-		<div class="pull-left media-body">
-			<div class="media-content-wrapper">
-				<div class="action-btns">${edit_html}</div>
-
-				<div class="comment-header clearfix">
-					<span class="pull-left avatar avatar-small visible-xs">
-						${image_html}
-					</span>
-
-					<div class="asset-details">
-						<span class="author-wrap">
-							<i class="octicon octicon-quote hidden-xs fa-fw"></i>
-							<span>${data.username}</span>
-						</span>
-						<a class="text-muted">
-							<span class="text-muted hidden-xs">&ndash;</span>
-							<span class="hidden-xs">${comment_when(data.modified)}</span>
-						</a>
-					</div>
-				</div>
-				<div class="reply timeline-content-show">
-					<div class="timeline-item-content">
-						<p class="text-muted">
-							${rating_html}
-						</p>
-						<h6 class="bold">${data.subject}</h6>
-						<p class="text-muted">
-							${data.content}
-						</p>
-					</div>
-				</div>
-			</div>
-		</div>
-	</div>`;
-}
-
-function get_rating_html(rating) {
-	let rating_html = ``;
-	for (var i = 0; i < 5; i++) {
-		let star_class = 'fa-star';
-		if (i >= rating) star_class = 'fa-star-o';
-		rating_html += `<i class='fa fa-fw ${star_class} star-icon' data-index=${i}></i>`;
-	}
-	return rating_html;
-}
-
-export {
-	get_review_html,
-    get_rating_html
-}
diff --git a/erpnext/public/js/hub/hub_call.js b/erpnext/public/js/hub/hub_call.js
deleted file mode 100644
index 5545a49..0000000
--- a/erpnext/public/js/hub/hub_call.js
+++ /dev/null
@@ -1,68 +0,0 @@
-frappe.provide('hub');
-frappe.provide('erpnext.hub');
-
-erpnext.hub.cache = {};
-hub.call = function call_hub_method(method, args={}, clear_cache_on_event) { // eslint-disable-line
-	return new Promise((resolve, reject) => {
-
-		// cache
-		const key = method + JSON.stringify(args);
-		if (erpnext.hub.cache[key]) {
-			resolve(erpnext.hub.cache[key]);
-		}
-
-		// cache invalidation
-		const clear_cache = () => delete erpnext.hub.cache[key];
-
-		if (!clear_cache_on_event) {
-			invalidate_after_5_mins(clear_cache);
-		} else {
-			erpnext.hub.on(clear_cache_on_event, () => {
-				clear_cache(key);
-			});
-		}
-
-		let res;
-		if (hub.is_server) {
-			res = frappe.call({
-				method: 'hub.hub.api.' + method,
-				args
-			});
-		} else {
-			res = frappe.call({
-				method: 'erpnext.hub_node.api.call_hub_method',
-				args: {
-					method,
-					params: args
-				}
-			});
-		}
-
-		res.then(r => {
-			if (r.message) {
-				const response = r.message;
-				if (response.error) {
-					frappe.throw({
-						title: __('Marketplace Error'),
-						message: response.error
-					});
-				}
-
-				erpnext.hub.cache[key] = response;
-				erpnext.hub.trigger(`response:${key}`, { response });
-				resolve(response);
-			}
-			reject(r);
-
-		}).fail(reject);
-	});
-};
-
-function invalidate_after_5_mins(clear_cache) {
-	// cache invalidation after 5 minutes
-	const timeout = 5 * 60 * 1000;
-
-	setTimeout(() => {
-		clear_cache();
-	}, timeout);
-}
diff --git a/erpnext/public/js/hub/hub_factory.js b/erpnext/public/js/hub/hub_factory.js
deleted file mode 100644
index 9c67c1c..0000000
--- a/erpnext/public/js/hub/hub_factory.js
+++ /dev/null
@@ -1,34 +0,0 @@
-frappe.provide('erpnext.hub');
-
-frappe.views.MarketplaceFactory = class MarketplaceFactory extends frappe.views.Factory {
-	show() {
-		is_marketplace_disabled()
-			.then(disabled => {
-				if (disabled) {
-					frappe.show_not_found('Marketplace');
-					return;
-				}
-
-				if (frappe.pages.marketplace) {
-					frappe.container.change_to('marketplace');
-					erpnext.hub.marketplace.refresh();
-				} else {
-					this.make('marketplace');
-				}
-			});
-	}
-
-	make(page_name) {
-		frappe.require('marketplace.bundle.js', () => {
-			erpnext.hub.marketplace = new erpnext.hub.Marketplace({
-				parent: this.make_page(true, page_name)
-			});
-		});
-	}
-};
-
-function is_marketplace_disabled() {
-	return frappe.call({
-		method: "erpnext.hub_node.doctype.marketplace_settings.marketplace_settings.is_marketplace_enabled"
-	}).then(r => r.message)
-}
diff --git a/erpnext/public/js/hub/marketplace.bundle.js b/erpnext/public/js/hub/marketplace.bundle.js
deleted file mode 100644
index a1596e0..0000000
--- a/erpnext/public/js/hub/marketplace.bundle.js
+++ /dev/null
@@ -1,225 +0,0 @@
-import Vue from 'vue/dist/vue.js';
-import './vue-plugins';
-
-// components
-import PageContainer from './PageContainer.vue';
-import Sidebar from './Sidebar.vue';
-import { ProfileDialog } from './components/profile_dialog';
-
-// helpers
-import './hub_call';
-
-frappe.provide('hub');
-frappe.provide('erpnext.hub');
-frappe.provide('frappe.route');
-
-frappe.utils.make_event_emitter(frappe.route);
-frappe.utils.make_event_emitter(erpnext.hub);
-
-erpnext.hub.Marketplace = class Marketplace {
-	constructor({ parent }) {
-		this.$parent = $(parent);
-		this.page = parent.page;
-
-		this.update_hub_settings().then(() => {
-
-			this.setup_header();
-			this.make_sidebar();
-			this.make_body();
-			this.setup_events();
-			this.refresh();
-
-			if (!hub.is_server) {
-				if (!hub.is_seller_registered()) {
-					this.page.set_primary_action('Become a Seller', this.show_register_dialog.bind(this))
-				} else {
-					this.page.set_secondary_action('Add Users', this.show_add_user_dialog.bind(this));
-				}
-			}
-		});
-	}
-
-	setup_header() {
-		if (hub.is_server) return;
-		this.page.set_title(__('Marketplace'));
-	}
-
-	setup_events() {
-		this.$parent.on('click', '[data-route]', (e) => {
-			const $target = $(e.currentTarget);
-			const route = $target.data().route;
-			frappe.set_route(route);
-		});
-
-		// generic action handler
-		this.$parent.on('click', '[data-action]', e => {
-			const $target = $(e.currentTarget);
-			const action = $target.data().action;
-
-			if (action && this[action]) {
-				this[action].apply(this, $target);
-			}
-		})
-	}
-
-	make_sidebar() {
-		this.$sidebar = this.$parent.find('.layout-side-section').addClass('hidden-xs');
-
-		new Vue({
-			el: $('<div>').appendTo(this.$sidebar)[0],
-			render: h => h(Sidebar)
-		});
-	}
-
-	make_body() {
-		this.$body = this.$parent.find('.layout-main-section');
-		this.$page_container = $('<div class="hub-page-container">').appendTo(this.$body);
-
-		new Vue({
-			el: '.hub-page-container',
-			render: h => h(PageContainer)
-		});
-
-		if (!hub.is_server) {
-			erpnext.hub.on('seller-registered', () => {
-				this.page.clear_primary_action();
-			});
-		}
-	}
-
-	refresh() {
-
-	}
-
-	show_register_dialog() {
-		if(frappe.session.user === 'Administrator') {
-			frappe.msgprint(__('You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.'));
-			return;
-		}
-
-		if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) {
-			frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to register on Marketplace.'));
-			return;
-		}
-
-		this.register_dialog = ProfileDialog(
-			__('Become a Seller'),
-			{
-				label: __('Register'),
-				on_submit: this.register_marketplace.bind(this)
-			}
-		);
-
-		this.register_dialog.show();
-	}
-
-	register_marketplace({company, company_description}) {
-		frappe.call({
-		    method: 'erpnext.hub_node.api.register_marketplace',
-		    args: {
-				company,
-				company_description
-			}
-		}).then((r) => {
-			if (r.message && r.message.ok) {
-				this.register_dialog.hide();
-
-				this.update_hub_settings()
-					.then(() => {
-						frappe.set_route('marketplace', 'publish');
-						erpnext.hub.trigger('seller-registered');
-					});
-			}
-		});
-	}
-
-	show_add_user_dialog() {
-		if (!is_subset(['System Manager', 'Item Manager'], frappe.user_roles)) {
-			frappe.msgprint(__('You need to be a user with System Manager and Item Manager roles to add users to Marketplace.'));
-			return;
-		}
-
-		this.get_unregistered_users()
-			.then(r => {
-				const user_list = r.message;
-
-				const d = new frappe.ui.Dialog({
-					title: __('Add Users to Marketplace'),
-					fields: [
-						{
-							label: __('Users'),
-							fieldname: 'users',
-							fieldtype: 'MultiSelect',
-							reqd: 1,
-							get_data() {
-								return user_list;
-							}
-						}
-					],
-					primary_action({ users }) {
-						const selected_users = users.split(',').map(d => d.trim()).filter(Boolean);
-
-						if (!selected_users.every(user => user_list.includes(user))) {
-							d.set_df_property('users', 'description', __('Some emails are invalid'));
-							return;
-						} else {
-							d.set_df_property('users', 'description', '');
-						}
-
-						frappe.call('erpnext.hub_node.api.register_users', {
-							user_list: selected_users
-						})
-						.then(r => {
-							d.hide();
-
-							if (r.message && r.message.length) {
-								frappe.show_alert(__('Added {0} users', [r.message.length]));
-							}
-						});
-					}
-				});
-
-				d.show();
-			});
-	}
-
-	get_unregistered_users() {
-		return frappe.call('erpnext.hub_node.api.get_unregistered_users')
-	}
-
-	update_hub_settings() {
-		return hub.get_settings().then(doc => {
-			hub.settings = doc;
-		});
-	}
-}
-
-Object.assign(hub, {
-	is_seller_registered() {
-		return hub.settings.registered;
-	},
-
-	is_user_registered() {
-		return this.is_seller_registered() && hub.settings.users
-			.filter(hub_user => hub_user.user === frappe.session.user)
-			.length === 1;
-	},
-
-	get_settings() {
-		if (frappe.session.user === 'Guest') {
-			return Promise.resolve({
-				registered: 0
-			});
-		}
-		return frappe.db.get_doc('Marketplace Settings');
-	}
-});
-
-/**
- * Returns true if list_a is subset of list_b
- * @param {Array} list_a
- * @param {Array} list_b
- */
-function is_subset(list_a, list_b) {
-	return list_a.every(item => list_b.includes(item));
-}
diff --git a/erpnext/public/js/hub/pages/Buying.vue b/erpnext/public/js/hub/pages/Buying.vue
deleted file mode 100644
index ebf593a..0000000
--- a/erpnext/public/js/hub/pages/Buying.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<template>
-	<div>
-		<section-header>
-			<h4>{{ __('Buying') }}</h4>
-		</section-header>
-		<div class="row" v-if="items && items.length">
-			<div class="col-md-7 margin-bottom"
-				v-for="item of items"
-				:key="item.name"
-			>
-				<item-list-card
-					:item="item"
-					v-route="'marketplace/buying/' + item.name"
-				>
-					<div slot="subtitle">
-						<span>{{ get_sender(item.recent_message) }}: </span>
-						<span>{{ item.recent_message.message | striphtml }}</span>
-					</div>
-				</item-list-card>
-			</div>
-		</div>
-		<empty-state v-else :message="__('This page keeps track of items you want to buy from sellers.')" :centered="false" />
-	</div>
-</template>
-<script>
-import EmptyState from '../components/EmptyState.vue';
-import SectionHeader from '../components/SectionHeader.vue';
-import ItemListCard from '../components/ItemListCard.vue';
-
-export default {
-	components: {
-		SectionHeader,
-		ItemListCard,
-		EmptyState
-	},
-	data() {
-		return {
-			items: null
-		}
-	},
-	created() {
-		this.get_items_for_messages()
-			.then(items => {
-				this.items = items;
-			});
-	},
-	methods: {
-		get_items_for_messages() {
-			return hub.call('get_buying_items_for_messages', {}, 'action:send_message');
-		},
-		get_sender(message) {
-			return message.sender === frappe.session.user ? __('You') : (message.sender_name || message.sender);
-		}
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue
deleted file mode 100644
index 16d0601..0000000
--- a/erpnext/public/js/hub/pages/Category.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<search-input
-			:placeholder="search_placeholder"
-			:on_search="set_search_route"
-			v-model="search_value"
-		/>
-
-		<h5>{{ page_title }}</h5>
-
-		<item-cards-container
-			:container_name="page_title"
-			:items="items"
-			:item_id_fieldname="item_id_fieldname"
-			:on_click="go_to_item_details_page"
-			:empty_state_message="empty_state_message"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			category: frappe.get_route()[2],
-			items: [],
-			item_id_fieldname: 'name',
-
-			// Constants
-			empty_state_message: __('No items in this category yet.'),
-
-			search_value: '',
-
-			// Constants
-			search_placeholder: __('Search for anything ...'),
-
-		};
-	},
-	computed: {
-		page_title() {
-			return __(this.category);
-		}
-	},
-	created() {
-		this.search_value = '';
-		this.get_items();
-	},
-	methods: {
-		get_items() {
-			hub.call('get_items', {
-				filters: {
-					hub_category: this.category
-				}
-			})
-			.then((items) => {
-				this.items = items;
-			})
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		},
-
-		set_search_route() {
-			frappe.set_route('marketplace', 'search', this.category, this.search_value);
-		},
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue
deleted file mode 100644
index 8380b2b..0000000
--- a/erpnext/public/js/hub/pages/FeaturedItems.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<h5>{{ page_title }}</h5>
-		<p v-if="items.length"
-			class="text-muted margin-bottom">
-			{{ __('You can Feature upto 8 items.') }}
-		</p>
-
-		<item-cards-container
-			:container_name="page_title"
-			:items="items"
-			:item_id_fieldname="item_id_fieldname"
-			:on_click="go_to_item_details_page"
-			:editable="true"
-			@remove-item="on_item_remove"
-			:empty_state_message="empty_state_message"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-export default {
-	name: 'featured-items-page',
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			items: [],
-			item_id_fieldname: 'name',
-
-			// Constants
-			page_title: __('Your Featured Items'),
-			empty_state_message: __('No featured items yet. Got to your {0} and feature up to eight items that you want to highlight to your customers.',
-				[`<a href="#marketplace/published-items">${__("Published Items")}</a>`])
-		};
-	},
-	created() {
-		this.get_items();
-	},
-	methods: {
-		get_items() {
-			hub.call(
-				'get_featured_items_of_seller', {},
-				'action:item_feature'
-			)
-			.then((items) => {
-				this.items = items;
-			})
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		},
-
-		on_item_remove(hub_item_name) {
-			const grace_period = 5000;
-			let reverted = false;
-			let alert;
-
-			const undo_remove = () => {
-				this.toggle_item(hub_item_name);;
-				reverted = true;
-				alert.hide();
-				return false;
-			}
-
-			const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
-
-			alert_message = __('{0} removed. {1}', [item_name,
-				`<a href="#" data-action="undo-remove"><b>${__('Undo')}</b></a>`]);
-			alert = frappe.show_alert(alert_message, grace_period / 1000,
-				{
-					'undo-remove': undo_remove.bind(this)
-				}
-			);
-
-			this.toggle_item(hub_item_name, false);
-
-			setTimeout(() => {
-				if(!reverted) {
-					this.remove_item_from_featured_items(hub_item_name);
-				}
-			}, grace_period);
-		},
-
-		remove_item_from_featured_items(hub_item_name) {
-			erpnext.hub.trigger('action:item_feature');
-			hub.call('remove_item_from_seller_featured_items', {
-				hub_item_name,
-				hub_user: frappe.session.user
-			})
-			.then(() => {
-				this.get_items();
-			})
-			.catch(e => {
-				console.log(e);
-			});
-		},
-
-		// By default show
-		toggle_item(hub_item_name, show=true) {
-			this.items = this.items.map(item => {
-				if(item.name === hub_item_name) {
-					item.seen = show;
-				}
-				return item;
-			});
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Home.vue b/erpnext/public/js/hub/pages/Home.vue
deleted file mode 100644
index 8fe8245..0000000
--- a/erpnext/public/js/hub/pages/Home.vue
+++ /dev/null
@@ -1,114 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<search-input
-			:placeholder="search_placeholder"
-			:on_search="set_search_route"
-			v-model="search_value"
-		/>
-
-		<div v-if="show_skeleton">
-			<section-header>
-				<h4 class="hub-skeleton">Explore Explore Explore</h4>
-			</section-header>
-			<div class="row">
-				<div class="col-md-3 col-sm-4 col-xs-6 hub-card-container" v-for="(f, $index) in [1, 2, 3, 4, 5, 6, 7]" :key="$index">
-					<div class="hub-skeleton" style="height: 262px; width: 100%; margin-bottom: 25px;"></div>
-				</div>
-			</div>
-		</div>
-
-		<div v-else v-for="section in sections" :key="section.title">
-
-			<section-header>
-				<h4>{{ section.title }}</h4>
-				<p v-if="section.expandable" :data-route="'marketplace/category/' + section.title">{{ 'See All' }}</p>
-			</section-header>
-
-			<item-cards-container
-				:container_name="section.title"
-				:items="section.items"
-				:item_id_fieldname="item_id_fieldname"
-				:on_click="go_to_item_details_page"
-			/>
-		</div>
-	</div>
-</template>
-
-<script>
-export default {
-	name: 'home-page',
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			item_id_fieldname: 'name',
-			search_value: '',
-
-			sections: [],
-			show_skeleton: true,
-
-			// Constants
-			search_placeholder: __('Search for anything ...'),
-		};
-	},
-	created() {
-		// refreshed
-		this.search_value = '';
-		this.get_items();
-	},
-	mounted() {
-		frappe.route.on('change', () => {
-			if (frappe.get_route_str() === 'marketplace/home') {
-				this.get_items();
-			}
-		})
-	},
-	methods: {
-		get_items() {
-			hub.call('get_data_for_homepage', frappe.defaults ? {
-				country: frappe.defaults.get_user_default('country')
-			} : null)
-			.then((data) => {
-				this.show_skeleton = false;
-
-				this.sections.push({
-					title: __('Explore'),
-					items: data.random_items
-				});
-				if (data.items_by_country.length) {
-					this.sections.push({
-						title: __('Near you'),
-						items: data.items_by_country
-					});
-				}
-
-				const category_items = data.category_items;
-
-				if (category_items) {
-					Object.keys(category_items).map(category => {
-						const items = category_items[category];
-
-						this.sections.push({
-							title: __(category),
-							expandable: true,
-							items
-						});
-					});
-				}
-			})
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		},
-
-		set_search_route() {
-			frappe.set_route('marketplace', 'search', 'All', this.search_value);
-		},
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue
deleted file mode 100644
index 93002a7..0000000
--- a/erpnext/public/js/hub/pages/Item.vue
+++ /dev/null
@@ -1,356 +0,0 @@
-<template>
-	<div class="marketplace-page" :data-page-name="page_name" v-if="init || item">
-		<detail-view
-			:title="title"
-			:image="image"
-			:sections="sections"
-			:menu_items="menu_items"
-			:show_skeleton="init"
-		>
-			<detail-header-item slot="detail-header-item" :value="item_subtitle"></detail-header-item>
-			<detail-header-item slot="detail-header-item" :value="item_views_and_ratings"></detail-header-item>
-
-			<button
-				v-if="primary_action"
-				slot="detail-header-item"
-				class="btn btn-primary btn-sm margin-top"
-				@click="primary_action.action"
-			>{{ primary_action.label }}</button>
-		</detail-view>
-
-		<review-area v-if="!init" :hub_item_name="hub_item_name"></review-area>
-	</div>
-</template>
-
-<script>
-import ReviewArea from '../components/ReviewArea.vue';
-import { get_rating_html } from '../components/reviews';
-import { edit_details_dialog } from '../components/edit_details_dialog';
-
-export default {
-	name: 'item-page',
-	components: {
-		ReviewArea
-	},
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			hub_item_name: frappe.get_route()[2],
-
-			init: true,
-
-			item: null,
-			title: null,
-			image: null,
-			sections: []
-		};
-	},
-	computed: {
-		is_own_item() {
-			let is_own_item = false;
-			if (this.item) {
-				if (this.item.hub_seller === hub.settings.hub_seller_name) {
-					is_own_item = true;
-				}
-			}
-			return is_own_item;
-		},
-		menu_items() {
-			return [
-				{
-					label: __('Save Item'),
-					condition: hub.is_user_registered() && !this.is_own_item,
-					action: this.add_to_saved_items
-				},
-				{
-					label: __('Add to Featured Item'),
-					condition: hub.is_user_registered() && this.is_own_item,
-					action: this.add_to_featured_items
-				},
-				{
-					label: __('Report this Item'),
-					condition: !this.is_own_item,
-					action: this.report_item
-				},
-				{
-					label: __('Edit Details'),
-					condition: hub.is_user_registered() && this.is_own_item,
-					action: this.edit_details
-				},
-				{
-					label: __('Unpublish Item'),
-					condition: hub.is_user_registered() && this.is_own_item,
-					action: this.unpublish_item
-				}
-			];
-		},
-
-		item_subtitle() {
-			if (!this.item) {
-				return '';
-			}
-
-			const dot_spacer = '<span aria-hidden="true"> · </span>';
-			let subtitle_items = [comment_when(this.item.creation)];
-			const rating = this.item.average_rating;
-
-			if (rating > 0) {
-				subtitle_items.push(rating + `<i class='fa fa-fw fa-star-o'></i>`);
-			}
-
-			subtitle_items.push({
-				value: this.item.company,
-				on_click: this.go_to_seller_profile_page
-			});
-
-			return subtitle_items;
-		},
-
-		item_views_and_ratings() {
-			if (!this.item) {
-				return '';
-			}
-
-			let stats = __('No views yet');
-			if (this.item.view_count) {
-				const views_message = __('{0} Views', [this.item.view_count]);
-
-				const rating_html = get_rating_html(this.item.average_rating);
-				const rating_count =
-					this.item.no_of_ratings > 0
-						? __('{0} reviews', [this.item.no_of_ratings])
-						: __('No reviews yet');
-
-				stats = [views_message, rating_html, rating_count];
-			}
-
-			return stats;
-		},
-
-		primary_action() {
-			if (hub.is_user_registered()) {
-				return {
-					label: __('Contact Seller'),
-					action: this.contact_seller.bind(this)
-				};
-			} else {
-				return undefined;
-			}
-		}
-	},
-	created() {
-		this.get_item_details();
-	},
-	mounted() {
-		// To record a single view per session, (later)
-		// erpnext.hub.item_view_cache = erpnext.hub.item_view_cache || [];
-		// if (erpnext.hub.item_view_cache.includes(this.hub_item_name)) {
-		// 	return;
-		// }
-
-		this.item_received.then(() => {
-			setTimeout(() => {
-				hub.call('add_item_view', {
-					hub_item_name: this.hub_item_name
-				});
-				// .then(() => {
-				// 	erpnext.hub.item_view_cache.push(this.hub_item_name);
-				// });
-			}, 5000);
-		});
-	},
-	methods: {
-		get_item_details() {
-			this.item_received = hub
-				.call('get_item_details', { hub_item_name: this.hub_item_name })
-				.then(item => {
-					this.init = false;
-					this.item = item;
-
-					this.build_data();
-					this.make_dialogs();
-				});
-		},
-		go_to_seller_profile_page(seller_name) {
-			frappe.set_route(`marketplace/seller/${seller_name}`);
-		},
-		build_data() {
-			this.title = this.item.item_name || this.item.name;
-			this.image = this.item.image;
-
-			this.sections = [
-				{
-					title: __('Item Description'),
-					content: this.item.description
-						? __(this.item.description)
-						: __('No description')
-				},
-				{
-					title: __('Seller Information'),
-					content: this.item.seller_description
-						? __(this.item.seller_description)
-						: __('No description')
-				}
-			];
-		},
-
-		make_dialogs() {
-			this.make_contact_seller_dialog();
-			this.make_report_item_dialog();
-			this.make_editing_dialog();
-		},
-
-		add_to_saved_items() {
-			hub.call('add_item_to_user_saved_items', {
-					hub_item_name: this.hub_item_name,
-					hub_user: frappe.session.user
-				})
-				.then(() => {
-					const saved_items_link = `<b><a href="#marketplace/saved-items">${__('Saved')}</a></b>`;
-					frappe.show_alert(saved_items_link);
-					erpnext.hub.trigger('action:item_save');
-				})
-				.catch(e => {
-					console.error(e);
-				});
-		},
-
-		add_to_featured_items() {
-			hub.call('add_item_to_seller_featured_items', {
-					hub_item_name: this.hub_item_name,
-					hub_user: frappe.session.user
-				})
-				.then(() => {
-					const featured_items_link = `<b><a href="#marketplace/featured-items">${__('Added to Featured Items')}</a></b>`;
-					frappe.show_alert(featured_items_link);
-					erpnext.hub.trigger('action:item_feature');
-				})
-				.catch(e => {
-					console.error(e);
-				});
-		},
-
-		make_contact_seller_dialog() {
-			this.contact_seller_dialog = new frappe.ui.Dialog({
-				title: __('Send a message'),
-				fields: [
-					{
-						fieldname: 'to',
-						fieldtype: 'Read Only',
-						label: __('To'),
-						default: this.item.company
-					},
-					{
-						fieldtype: 'Text',
-						fieldname: 'message',
-						label: __('Message')
-					}
-				],
-				primary_action: ({ message }) => {
-					if (!message) return;
-
-					hub.call('send_message', {
-							hub_item: this.item.name,
-							message
-						})
-						.then(() => {
-							this.contact_seller_dialog.hide();
-							frappe.set_route('marketplace', 'buying', this.item.name);
-							erpnext.hub.trigger('action:send_message');
-						});
-				}
-			});
-		},
-
-		make_report_item_dialog() {
-			this.report_item_dialog = new frappe.ui.Dialog({
-				title: __('Report Item'),
-				fields: [
-					{
-						label: __('Why do think this Item should be removed?'),
-						fieldtype: 'Text',
-						fieldname: 'message'
-					}
-				],
-				primary_action: ({ message }) => {
-					hub.call('add_reported_item', {
-							hub_item_name: this.item.name,
-							message
-						})
-						.then(() => {
-							d.hide();
-							frappe.show_alert(__('Item Reported'));
-						});
-				}
-			});
-		},
-
-		make_editing_dialog() {
-			this.edit_dialog = edit_details_dialog({
-				primary_action: {
-					fn: values => {
-						this.update_details(values);
-						this.edit_dialog.hide();
-					}
-				},
-				defaults: {
-					item_name: this.item.item_name,
-					hub_category: this.item.hub_category,
-					description: this.item.description
-				}
-			});
-		},
-
-		update_details(values) {
-			frappe.call('erpnext.hub_node.api.update_item', {
-					ref_doc: this.item.name,
-					data: values
-				})
-				.then(r => {
-					return this.get_item_details();
-				})
-				.then(() => {
-					frappe.show_alert(__('{0} Updated', [this.item.item_name]));
-				});
-		},
-
-		contact_seller() {
-			this.contact_seller_dialog.show();
-		},
-
-		report_item() {
-			if (!hub.is_seller_registered()) {
-				frappe.throw(
-					__('Please login as a Marketplace User to report this item.')
-				);
-			}
-			this.report_item_dialog.show();
-		},
-
-		edit_details() {
-			if (!hub.is_seller_registered()) {
-				frappe.throw(
-					__('Please login as a Marketplace User to edit this item.')
-				);
-			}
-			this.edit_dialog.show();
-		},
-
-		unpublish_item() {
-			frappe.confirm(__('Unpublish {0}?', [this.item.item_name]), () => {
-				frappe
-					.call('erpnext.hub_node.api.unpublish_item', {
-						item_code: this.item.item_code,
-						hub_item_name: this.hub_item_name
-					})
-					.then(r => {
-						frappe.set_route(`marketplace/home`);
-						frappe.show_alert(__('Item listing removed'));
-					});
-			});
-		}
-	}
-};
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Messages.vue b/erpnext/public/js/hub/pages/Messages.vue
deleted file mode 100644
index 73506e9..0000000
--- a/erpnext/public/js/hub/pages/Messages.vue
+++ /dev/null
@@ -1,104 +0,0 @@
-<template>
-	<div v-if="item_details">
-		<div>
-			<a class="text-muted" v-route="back_link">← {{ __('Back to Messages') }}</a>
-		</div>
-		<section-header>
-			<div class="flex flex-column margin-bottom">
-				<h4>{{ item_details.item_name }}</h4>
-				<span class="text-muted">{{ item_details.company }}</span>
-			</div>
-		</section-header>
-		<div class="row">
-			<div class="col-md-7">
-				<div class="message-container">
-					<div class="message-list">
-						<div class="level margin-bottom" v-for="message in messages" :key="message.name">
-							<div class="level-left ellipsis" style="width: 80%;">
-								<div v-html="frappe.avatar(message.sender)" />
-								<div style="white-space: normal;" v-html="message.message" />
-							</div>
-							<div class="level-right text-muted" v-html="frappe.datetime.comment_when(message.creation, true)" />
-						</div>
-					</div>
-					<div class="message-input">
-						<comment-input @change="send_message" />
-					</div>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-<script>
-import CommentInput from '../components/CommentInput.vue';
-import ItemListCard from '../components/ItemListCard.vue';
-
-export default {
-	components: {
-		CommentInput,
-		ItemListCard
-	},
-	data() {
-		return {
-			message_type: frappe.get_route()[1],
-			item_details: null,
-			messages: []
-		}
-	},
-	created() {
-		const hub_item_name = this.get_hub_item_name();
-		this.get_item_details(hub_item_name)
-			.then(item_details => {
-				this.item_details = item_details;
-				this.get_messages()
-					.then(messages => {
-						this.messages = messages;
-					});
-			});
-	},
-	computed: {
-		back_link() {
-			return 'marketplace/' + this.message_type;
-		}
-	},
-	methods: {
-		send_message(message) {
-			this.messages.push({
-				sender: frappe.session.user,
-				message: message,
-				creation: Date.now(),
-				name: frappe.utils.get_random(6)
-			});
-			hub.call('send_message', {
-				to_seller: this.get_against_seller(),
-				hub_item: this.item_details.name,
-				message
-			});
-		},
-		get_item_details(hub_item_name) {
-			return hub.call('get_item_details', { hub_item_name })
-		},
-		get_messages() {
-			if (!this.item_details) return [];
-			return hub.call('get_messages', {
-				against_seller: this.get_against_seller(),
-				against_item: this.item_details.name
-			});
-		},
-		get_against_seller() {
-			if (this.message_type === 'buying') {
-				return this.item_details.hub_seller;
-			} else if (this.message_type === 'selling') {
-				return frappe.get_route()[2];
-			}
-		},
-		get_hub_item_name() {
-			if (this.message_type === 'buying') {
-				return frappe.get_route()[2];
-			} else if (this.message_type === 'selling') {
-				return frappe.get_route()[3];
-			}
-		}
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/pages/NotFound.vue b/erpnext/public/js/hub/pages/NotFound.vue
deleted file mode 100644
index 8901b97..0000000
--- a/erpnext/public/js/hub/pages/NotFound.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<empty-state
-			:message="empty_state_message"
-			:height="500"
-			:action="action"
-		>
-		</empty-state>
-
-	</div>
-</template>
-
-<script>
-export default {
-	name: 'not-found-page',
-	data() {
-		return {
-			page_name: 'not-found',
-			action: {
-				label: __('Back to Home'),
-				on_click: () => {
-					frappe.set_route(`marketplace/home`);
-				}
-			},
-
-			// Constants
-			empty_state_message: __('Sorry! We could not find what you were looking for.')
-		};
-	},
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue
deleted file mode 100644
index ecba4b1..0000000
--- a/erpnext/public/js/hub/pages/Publish.vue
+++ /dev/null
@@ -1,212 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<notification-message
-			v-if="last_sync_message"
-			:message="last_sync_message"
-			@remove-message="clear_last_sync_message"
-		></notification-message>
-
-		<div class="flex justify-between align-flex-end margin-bottom">
-			<h5>{{ page_title }}</h5>
-
-			<button class="btn btn-primary btn-sm publish-items"
-				:disabled="no_selected_items"
-				@click="publish_selected_items"
-			>
-				<span>{{ publish_button_text }}</span>
-			</button>
-		</div>
-
-		<item-cards-container
-			:container_name="page_title"
-			:items="selected_items"
-			:item_id_fieldname="item_id_fieldname"
-			:is_local="true"
-			:editable="true"
-			@remove-item="remove_item_from_selection"
-
-			:empty_state_message="empty_state_message"
-			:empty_state_bordered="true"
-			:empty_state_height="80"
-		>
-		</item-cards-container>
-
-		<p class="text-muted">{{ valid_items_instruction }}</p>
-
-		<search-input
-			:placeholder="search_placeholder"
-			:on_search="get_valid_items"
-			v-model="search_value"
-		>
-		</search-input>
-
-		<item-cards-container
-			:items="valid_items"
-			:item_id_fieldname="item_id_fieldname"
-			:is_local="true"
-			:on_click="show_publishing_dialog_for_item"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-import NotificationMessage from '../components/NotificationMessage.vue';
-import { ItemPublishDialog } from '../components/item_publish_dialog';
-
-export default {
-	name: 'publish-page',
-	components: {
-		NotificationMessage
-	},
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			valid_items: [],
-			selected_items: [],
-			items_data_to_publish: {},
-			search_value: '',
-			item_id_fieldname: 'item_code',
-
-			// Constants
-			// TODO: multiline translations don't work
-			page_title: __('Publish Items'),
-			search_placeholder: __('Search Items ...'),
-			empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'),
-			valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'),
-			last_sync_message: (hub.settings.last_sync_datetime)
-				? __('Last sync was {0}.', [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]) +
-				  ` <a href="#marketplace/published-items">${__('See your Published Items.')}</a>`
-				: ''
-		};
-	},
-	computed: {
-		no_selected_items() {
-			return this.selected_items.length === 0;
-		},
-
-		publish_button_text() {
-			const number = this.selected_items.length;
-			let text = __('Publish');
-			if(number === 1) {
-				text = __('Publish 1 Item');
-			}
-			if(number > 1) {
-				text = __('Publish {0} Items', [number]);
-			}
-			return text;
-		},
-
-		items_dict() {
-			let items_dict = {};
-			this.valid_items.map(item => {
-				items_dict[item[this.item_id_fieldname]] = item
-			})
-
-			return items_dict;
-		},
-	},
-	created() {
-		this.get_valid_items();
-		this.make_publishing_dialog();
-	},
-	methods: {
-		get_valid_items() {
-			frappe.call(
-				'erpnext.hub_node.api.get_valid_items',
-				{
-					search_value: this.search_value
-				}
-			)
-			.then((r) => {
-				this.valid_items = r.message;
-			})
-		},
-
-		publish_selected_items() {
-			frappe.call(
-			'erpnext.hub_node.api.publish_selected_items',
-				{
-					items_to_publish: this.selected_items
-				}
-			)
-			.then((r) => {
-				this.selected_items = [];
-				return frappe.db.get_doc('Marketplace Settings');
-			})
-			.then(doc => {
-				hub.settings = doc;
-				this.add_last_sync_message();
-			});
-		},
-
-		add_last_sync_message() {
-			this.last_sync_message = __('Last sync was {0}.',
-				[`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]
-			) + `<a href="#marketplace/published-items">${__('See your Published Items')}</a>.`;
-		},
-
-		clear_last_sync_message() {
-			this.last_sync_message = '';
-		},
-
-		remove_item_from_selection(item_code) {
-			this.selected_items = this.selected_items
-				.filter(item => item.item_code !== item_code);
-		},
-
-		make_publishing_dialog() {
-			this.item_publish_dialog = ItemPublishDialog(
-				{
-					fn: (values) => {
-						this.add_item_to_publish(values);
-						this.item_publish_dialog.hide();
-					}
-				},
-				{
-					fn: () => {
-						const values = this.item_publish_dialog.get_values(true);
-						this.update_items_data_to_publish(values);
-					}
-				}
-			);
-		},
-
-		add_item_to_publish(values) {
-			this.update_items_data_to_publish(values);
-
-			const item_code  = values.item_code;
-			let item_doc = this.items_dict[item_code];
-
-			const item_to_publish = Object.assign({}, item_doc, values);
-			this.selected_items.push(item_to_publish);
-		},
-
-		update_items_data_to_publish(values) {
-			this.items_data_to_publish[values.item_code] = values;
-		},
-
-		show_publishing_dialog_for_item(item_code) {
-			let item_data = this.items_data_to_publish[item_code];
-			if(!item_data) { item_data = { item_code }; };
-
-			this.item_publish_dialog.clear();
-
-			const item_doc = this.items_dict[item_code];
-			if(item_doc) {
-				this.item_publish_dialog.fields_dict.image_list.set_data(
-					item_doc.attachments.map(attachment => attachment.file_url)
-				);
-			}
-
-			this.item_publish_dialog.set_values(item_data);
-			this.item_publish_dialog.show();
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/PublishedItems.vue b/erpnext/public/js/hub/pages/PublishedItems.vue
deleted file mode 100644
index cbb2216..0000000
--- a/erpnext/public/js/hub/pages/PublishedItems.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<section-header>
-			<div>
-				<h5>{{ __('Published Items') }}</h5>
-				<p v-if="items.length"
-					class="text-muted margin-bottom">
-					{{ __('You can publish upto 200 items.') }}
-				</p>
-			</div>
-
-			<button v-if="items.length"
-				class="btn btn-default btn-xs publish-items"
-				v-route="'marketplace/publish'"
-			>
-				<span>{{ __('Publish More Items') }}</span>
-			</button>
-
-		</section-header>
-
-		<item-cards-container
-			:container_name="__('Published Items')"
-			:items="items"
-			:item_id_fieldname="item_id_fieldname"
-			:on_click="go_to_item_details_page"
-			:empty_state_message="__('You haven\'t published any items yet.')"
-			:empty_state_action="publish_page_action"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			items: [],
-			item_id_fieldname: 'name',
-
-			publish_page_action: {
-				label: __('Publish Your First Items'),
-				on_click: () => {
-					frappe.set_route(`marketplace/publish`);
-				}
-			}
-		};
-	},
-	created() {
-		this.get_items();
-	},
-	methods: {
-		get_items() {
-			hub.call('get_items', {
-				filters: {
-					hub_seller: hub.settings.hub_seller_name
-				}
-			})
-			.then((items) => {
-				this.items = items;
-			})
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/SavedItems.vue b/erpnext/public/js/hub/pages/SavedItems.vue
deleted file mode 100644
index 7007ddc..0000000
--- a/erpnext/public/js/hub/pages/SavedItems.vue
+++ /dev/null
@@ -1,116 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<h5>{{ page_title }}</h5>
-
-		<item-cards-container
-			:container_name="page_title"
-			:items="items"
-			:item_id_fieldname="item_id_fieldname"
-			:on_click="go_to_item_details_page"
-			:editable="true"
-			@remove-item="on_item_remove"
-			:empty_state_message="empty_state_message"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-export default {
-	name: 'saved-items-page',
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			items: [],
-			item_id_fieldname: 'name',
-
-			// Constants
-			page_title: __('Saved Items'),
-			empty_state_message: __('You have not saved any items yet.')
-		};
-	},
-	created() {
-		this.get_items();
-	},
-	methods: {
-		get_items() {
-			hub.call(
-				'get_saved_items_of_user', {},
-				'action:item_save'
-			)
-			.then((items) => {
-				this.items = items;
-			})
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		},
-
-		on_item_remove(hub_item_name) {
-			const grace_period = 5000;
-			let reverted = false;
-			let alert;
-
-			const undo_remove = () => {
-				this.toggle_item(hub_item_name);;
-				reverted = true;
-				alert.hide();
-				return false;
-			}
-
-			const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
-
-			alert = frappe.show_alert(`
-				<span>
-					${__('{0} removed.', [item_name], 'A specific Item has been removed.')}
-					<a href="#" data-action="undo-remove">
-						<b>${__('Undo', None, 'Undo removal of item.')}</b>
-					</a>
-				</span>`,
-				grace_period/1000,
-				{
-					'undo-remove': undo_remove.bind(this)
-				}
-			);
-
-			this.toggle_item(hub_item_name, false);
-
-			setTimeout(() => {
-				if(!reverted) {
-					this.remove_item_from_saved_items(hub_item_name);
-				}
-			}, grace_period);
-		},
-
-		remove_item_from_saved_items(hub_item_name) {
-			erpnext.hub.trigger('action:item_save');
-			hub.call('remove_item_from_user_saved_items', {
-				hub_item_name,
-				hub_user: frappe.session.user
-			})
-			.then(() => {
-				this.get_items();
-			})
-			.catch(e => {
-				console.log(e);
-			});
-		},
-
-		// By default show
-		toggle_item(hub_item_name, show=true) {
-			this.items = this.items.map(item => {
-				if(item.name === hub_item_name) {
-					item.seen = show;
-				}
-				return item;
-			});
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Search.vue b/erpnext/public/js/hub/pages/Search.vue
deleted file mode 100644
index c10841e..0000000
--- a/erpnext/public/js/hub/pages/Search.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-	>
-		<search-input
-			:placeholder="search_placeholder"
-			:on_search="set_route_and_get_items"
-			v-model="search_value"
-		>
-		</search-input>
-
-		<h5>{{ page_title }}</h5>
-
-		<item-cards-container
-			container_name="Search"
-			:items="items"
-			:item_id_fieldname="item_id_fieldname"
-			:on_click="go_to_item_details_page"
-			:empty_state_message="empty_state_message"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-export default {
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			items: [],
-			category: frappe.get_route()[2],
-			search_value: frappe.get_route()[3],
-			item_id_fieldname: 'name',
-			filters: {},
-
-			// Constants
-			search_placeholder: __('Search for anything ...'),
-			empty_state_message: __('')
-		};
-	},
-	computed: {
-		page_title() {
-			return this.items.length
-				? __('Results for "{0}" {1}', [
-					this.search_value,
-					this.category !== 'All' ? __('in category {0}', [this.category]) : ''
-				  ])
-				: __('No Items found.');
-		}
-	},
-	created() {
-		this.get_items();
-	},
-	methods: {
-		get_items() {
-			if (this.category !== 'All') {
-				this.filters['hub_category'] = this.category;
-			}
-			hub.call('get_items', {
-				keyword: this.search_value,
-				filters: this.filters
-			})
-			.then((items) => {
-				this.items = items;
-			})
-		},
-
-		set_route_and_get_items() {
-			frappe.set_route('marketplace', 'search', this.category, this.search_value);
-			this.get_items();
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue
deleted file mode 100644
index 3c9b800..0000000
--- a/erpnext/public/js/hub/pages/Seller.vue
+++ /dev/null
@@ -1,201 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-		v-if="init || profile"
-	>
-		<detail-view
-			:title="title"
-			:image="image"
-			:sections="sections"
-			:show_skeleton="init"
-		>
-			<detail-header-item slot="detail-header-item"
-				:value="country"
-			></detail-header-item>
-			<detail-header-item slot="detail-header-item"
-				:value="site_name"
-			></detail-header-item>
-			<detail-header-item slot="detail-header-item"
-				:value="joined_when"
-			></detail-header-item>
-
-		</detail-view>
-
-		<div v-if="items.length">
-			<h5>
-				{{ item_container_heading }}
-				<small v-if="is_user_registered() && is_own_company">
-					<a class="pull-right" href="#marketplace/featured-items">Customize your Featured Items</a>
-				</small>
-			</h5>
-			<item-cards-container
-				:container_name="item_container_heading"
-				:items="items"
-				:item_id_fieldname="item_id_fieldname"
-				:on_click="go_to_item_details_page"
-			>
-			</item-cards-container>
-			<a class="pull-right" @click="go_to_seller_items_page(seller_company)">Show all items</a>
-		</div>
-
-		<div v-if="recent_seller_reviews.length">
-			<h5>Customer Reviews</h5>
-			<div class="container" v-for="review in recent_seller_reviews" :key="review.name">
-				<br>
-				<span class="text-muted">
-					<rating :rating="review.rating" :max_rating="5"></rating>
-				</span>
-				<i class="octicon octicon-quote hidden-xs fa-fw"></i>
-				<span class="bold">{{ review.subject }}</span>
-				<i class="octicon octicon-quote hidden-xs fa-fw fa-rotate-180"></i>
-				<div class="container">
-					by {{ review.username }}
-					<a class="text-muted">
-						<span class="text-muted hidden-xs">&ndash;</span>
-						<span class="hidden-xs" v-html="comment_when(review.timestamp)"></span>
-					</a>
-				</div>
-			</div>
-		</div>
-
-		<div v-if="seller_product_view_stats.length">
-			<h5>Stats</h5>
-			<div id="seller_traffic_chart"></div>
-		</div>
-
-
-
-	</div>
-</template>
-
-<script>
-import Rating from '../components/Rating.vue';
-
-
-export default {
-	name: 'seller-page',
-	components: {
-        Rating
-    },
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			seller_company: frappe.get_route()[2],
-			hub_seller: null,
-
-			init: true,
-
-			profile: null,
-			items:[],
-			recent_seller_reviews: [],
-			seller_product_view_stats: [],
-			seller_traffic_chart: null,
-			item_id_fieldname: 'name',
-			item_container_heading: 'Items',
-
-			title: null,
-			image: null,
-			sections: [],
-
-			country: '',
-			site_name: '',
-			joined_when: '',
-		};
-	},
-	created() {
-		this.get_seller_profile_and_items();
-	},
-	computed: {
-		is_own_company() {
-			let is_own_company = false;
-			if(this.hub_seller) {
-				if(this.hub_seller === hub.settings.hub_seller_name) {
-					is_own_company = true;
-				}
-			}
-			return is_own_company;
-		},
-	},
-	methods: {
-		comment_when(timestamp){
-			return comment_when(timestamp)
-		},
-		is_user_registered(){
-			return hub.is_user_registered()
-		},
-		get_seller_profile_and_items() {
-			let post_data = {company: this.seller_company}
-			if (this.page_name == 'profile'){
-				this.seller_company = null;
-				this.hub_seller = hub.settings.hub_seller_name
-				post_data = {hub_seller: this.hub_seller}
-			}
-			hub.call('get_hub_seller_page_info', post_data)
-			.then(data => {
-				this.init = false;
-				this.profile = data.profile;
-				this.items = data.items;
-				this.item_container_heading = data.is_featured_item ? __('Featured Items') : __('Popular Items');
-				this.hub_seller = this.items[0].hub_seller;
-				this.recent_seller_reviews = data.recent_seller_reviews;
-				this.seller_product_view_stats = data.seller_product_view_stats;
-
-				const profile = this.profile;
-
-				this.title = profile.company;
-
-				this.country = __(profile.country);
-				this.site_name = __(profile.site_name);
-				this.joined_when = __('Joined {0}', [comment_when(profile.creation)]);
-
-				this.image = profile.logo;
-				this.sections = [
-					{
-						title: __('About the Company'),
-						content: profile.company_description
-							? __(profile.company_description)
-							: __('No description')
-					}
-				];
-
-				setTimeout(() => this.init_seller_traffic_chart(), 1);
-
-			});
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		},
-		go_to_seller_items_page(hub_seller) {
-			frappe.set_route(`marketplace/seller/${hub_seller}/items`);
-		},
-		init_seller_traffic_chart() {
-			let lables = []
-			let tooltip_lables = {}
-			let datasets = [{name:"Product Views",chartType: 'line',values: []}]
-			this.seller_product_view_stats.map((stat) => {
-				lables.push(stat.date.substring(5));
-				tooltip_lables[stat.date.substring(5)] = new Date(stat.date).toDateString();
-				datasets[0].values.push(stat.view_count);
-			});
-			let data = {labels: lables, datasets:datasets, tooltip_lables:tooltip_lables}
-			this.seller_traffic_chart = new Chart( "#seller_traffic_chart", { // or DOM element
-			data: data,
-
-			title: "Daily Product Views",
-			type: 'axis-mixed', // or 'bar', 'line', 'pie', 'percentage'
-			height: 300,
-			colors: ['purple', '#ffa3ef', 'light-blue'],
-
-			tooltipOptions: {
-			formatTooltipX: d => this.seller_traffic_chart.data.tooltip_lables[d],
-			formatTooltipY: d => d + ' Views',
-			}
-		});
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/SellerItems.vue b/erpnext/public/js/hub/pages/SellerItems.vue
deleted file mode 100644
index 852fbae..0000000
--- a/erpnext/public/js/hub/pages/SellerItems.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<template>
-	<div
-		class="marketplace-page"
-		:data-page-name="page_name"
-		v-if="init || items.length"
-	>
-		<h5>{{ item_container_heading }}</h5>
-		<item-cards-container
-			:container_name="item_container_heading"
-			:items="items"
-			:item_id_fieldname="item_id_fieldname"
-			:on_click="go_to_item_details_page"
-		>
-		</item-cards-container>
-	</div>
-</template>
-
-<script>
-export default {
-	name: 'seller-items-page',
-	data() {
-		return {
-			page_name: frappe.get_route()[1],
-			seller_company: frappe.get_route()[2],
-
-			init: true,
-			items:[],
-			item_id_fieldname: 'name',
-		};
-	},
-	created() {
-		this.get_seller_and_items();
-	},
-	computed: {
-		item_container_heading() {
-			return __('Items by ' + this.seller_company);
-		}
-	},
-	methods: {
-		get_seller_and_items() {
-			hub.call(
-				'get_items',
-				{ company: this.seller_company }
-			).then(data => {
-				this.init = false;
-				this.items = data;
-			});
-		},
-
-		go_to_item_details_page(hub_item_name) {
-			frappe.set_route(`marketplace/item/${hub_item_name}`);
-		}
-	}
-}
-</script>
-
-<style scoped></style>
diff --git a/erpnext/public/js/hub/pages/Selling.vue b/erpnext/public/js/hub/pages/Selling.vue
deleted file mode 100644
index 8743027..0000000
--- a/erpnext/public/js/hub/pages/Selling.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<template>
-	<div>
-		<section-header>
-			<h4>{{ __('Selling') }}</h4>
-		</section-header>
-		<div class="row" v-if="items && items.length">
-			<div class="col-md-7"
-				style="margin-bottom: 30px;"
-				v-for="item of items"
-				:key="item.name"
-			>
-				<item-list-card
-					:item="item"
-				>
-					<div slot="subtitle">
-						<span class="text-muted">{{ __('{0} conversations', [item.received_messages.length]) }}</span>
-					</div>
-				</item-list-card>
-				<div class="hub-list-item" v-for="(message, index) in item.received_messages" :key="index"
-					v-route="'marketplace/selling/' + message.buyer + '/' + item.name"
-				>
-					<div class="hub-list-left">
-						<div class="hub-list-body">
-							<div class="hub-list-title">
-								{{ message.buyer_name }}
-							</div>
-							<div class="hub-list-subtitle">
-								{{ message.sender }}: {{ message.message | striphtml }}
-							</div>
-						</div>
-					</div>
-				</div>
-			</div>
-		</div>
-		<empty-state v-else :message="__('This page keeps track of your items in which buyers have showed some interest.')" :centered="false" />
-	</div>
-</template>
-<script>
-import EmptyState from '../components/EmptyState.vue';
-import SectionHeader from '../components/SectionHeader.vue';
-import ItemListCard from '../components/ItemListCard.vue';
-
-export default {
-	components: {
-		SectionHeader,
-		ItemListCard,
-		EmptyState
-	},
-	data() {
-		return {
-			items: null
-		}
-	},
-	created() {
-		this.get_items_for_messages()
-			.then(items => {
-				this.items = items;
-			});
-	},
-	methods: {
-		get_items_for_messages() {
-			return hub.call('get_selling_items_for_messages');
-		}
-	}
-}
-</script>
diff --git a/erpnext/public/js/hub/vue-plugins.js b/erpnext/public/js/hub/vue-plugins.js
deleted file mode 100644
index 4912d68..0000000
--- a/erpnext/public/js/hub/vue-plugins.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import Vue from 'vue/dist/vue.js';
-
-// Global components
-import ItemCardsContainer from './components/ItemCardsContainer.vue';
-import SectionHeader from './components/SectionHeader.vue';
-import SearchInput from './components/SearchInput.vue';
-import DetailView from './components/DetailView.vue';
-import DetailHeaderItem from './components/DetailHeaderItem.vue';
-import EmptyState from './components/EmptyState.vue';
-import Image from './components/Image.vue';
-
-Vue.prototype.__ = window.__;
-Vue.prototype.frappe = window.frappe;
-
-Vue.component('item-cards-container', ItemCardsContainer);
-Vue.component('section-header', SectionHeader);
-Vue.component('search-input', SearchInput);
-Vue.component('detail-view', DetailView);
-Vue.component('detail-header-item', DetailHeaderItem);
-Vue.component('empty-state', EmptyState);
-Vue.component('base-image', Image);
-
-Vue.directive('route', {
-	bind(el, binding) {
-		const route = binding.value;
-		if (!route) return;
-		el.classList.add('cursor-pointer');
-		el.dataset.route = route;
-		el.addEventListener('click', () => frappe.set_route(route));
-	},
-	unbind(el) {
-		el.classList.remove('cursor-pointer');
-	}
-});
-
-const handleImage = (el, src) => {
-	let img = new Image();
-	// add loading class
-	el.src = '';
-	el.classList.add('img-loading');
-
-	img.onload = () => {
-		// image loaded, remove loading class
-		el.classList.remove('img-loading');
-		// set src
-		el.src = src;
-	}
-	img.onerror = () => {
-		el.classList.remove('img-loading');
-		el.classList.add('no-image');
-		el.src = null;
-	}
-	img.src = src;
-}
-
-Vue.filter('striphtml', function (text) {
-	return strip_html(text || '');
-});
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
index f48fe6f..c32ab6b 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.json
@@ -7,7 +7,7 @@
  "engine": "InnoDB",
  "field_order": [
   "certificate_details_section",
-  "section_code",
+  "tax_withholding_category",
   "fiscal_year",
   "column_break_3",
   "certificate_no",
@@ -34,13 +34,6 @@
    "unique": 1
   },
   {
-   "fieldname": "section_code",
-   "fieldtype": "Select",
-   "label": "Section Code",
-   "options": "192\n193\n194\n194A\n194C\n194D\n194H\n194I\n194J\n194LA\n194LBB\n194LBC\n195",
-   "reqd": 1
-  },
-  {
    "fieldname": "section_break_3",
    "fieldtype": "Section Break",
    "label": "Deductee Details"
@@ -123,13 +116,22 @@
    "label": "Fiscal Year",
    "options": "Fiscal Year",
    "reqd": 1
+  },
+  {
+   "fieldname": "tax_withholding_category",
+   "fieldtype": "Link",
+   "label": "Tax Withholding Category",
+   "options": "Tax Withholding Category",
+   "reqd": 1
   }
  ],
+ "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2020-04-23 23:04:41.203721",
+ "modified": "2021-10-23 18:33:38.962622",
  "modified_by": "Administrator",
  "module": "Regional",
  "name": "Lower Deduction Certificate",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [],
  "sort_field": "modified",
diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
index d8553f1..7afbc00 100644
--- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
+++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py
@@ -15,7 +15,7 @@
 class LowerDeductionCertificate(Document):
 	def validate(self):
 		self.validate_dates()
-		self.validate_supplier_against_section_code()
+		self.validate_supplier_against_tax_category()
 
 	def validate_dates(self):
 		if getdate(self.valid_upto) < getdate(self.valid_from):
@@ -31,12 +31,14 @@
 			<= fiscal_year.year_end_date):
 			frappe.throw(_("Valid Upto date not in Fiscal Year {0}").format(frappe.bold(self.fiscal_year)))
 
-	def validate_supplier_against_section_code(self):
-		duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate', {'supplier': self.supplier, 'section_code': self.section_code}, ['name', 'valid_from', 'valid_upto'], as_dict=True)
+	def tax_withholding_category(self):
+		duplicate_certificate = frappe.db.get_value('Lower Deduction Certificate',
+			{'supplier': self.supplier, 'tax_withholding_category': self.tax_withholding_category, 'name': ("!=", self.name)},
+			['name', 'valid_from', 'valid_upto'], as_dict=True)
 		if duplicate_certificate and self.are_dates_overlapping(duplicate_certificate):
 			certificate_link = get_link_to_form('Lower Deduction Certificate', duplicate_certificate.name)
-			frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against Section Code {2} for this time period.")
-				.format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.section_code)))
+			frappe.throw(_("There is already a valid Lower Deduction Certificate {0} for Supplier {1} against category {2} for this time period.")
+				.format(certificate_link, frappe.bold(self.supplier), frappe.bold(self.tax_withholding_category)))
 
 	def are_dates_overlapping(self,duplicate_certificate):
 		valid_from = duplicate_certificate.valid_from
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 0e41280..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):
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.js b/erpnext/selling/doctype/sales_order/sales_order.js
index f692690..d46c46f 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -78,6 +78,8 @@
 		});
 
 		erpnext.queries.setup_warehouse_query(frm);
+
+		frm.ignore_doctypes_on_cancel_all = ['Purchase Order'];
 	},
 
 	delivery_date: function(frm) {
diff --git a/erpnext/hub/__init__.py b/erpnext/selling/print_format_field_template/__init__.py
similarity index 100%
copy from erpnext/hub/__init__.py
copy to erpnext/selling/print_format_field_template/__init__.py
diff --git a/erpnext/hub_node/doctype/hub_users/__init__.py b/erpnext/selling/print_format_field_template/quotation_taxes/__init__.py
similarity index 100%
rename from erpnext/hub_node/doctype/hub_users/__init__.py
rename to erpnext/selling/print_format_field_template/quotation_taxes/__init__.py
diff --git a/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json b/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json
new file mode 100644
index 0000000..eaa17ce
--- /dev/null
+++ b/erpnext/selling/print_format_field_template/quotation_taxes/quotation_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 15:48:56.416449",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Quotation",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:11:33.553722",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Quotation Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/hub/__init__.py b/erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py
similarity index 100%
copy from erpnext/hub/__init__.py
copy to erpnext/selling/print_format_field_template/sales_order_taxes/__init__.py
diff --git a/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json b/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json
new file mode 100644
index 0000000..2aacb44
--- /dev/null
+++ b/erpnext/selling/print_format_field_template/sales_order_taxes/sales_order_taxes.json
@@ -0,0 +1,16 @@
+{
+ "creation": "2021-10-19 18:04:24.443076",
+ "docstatus": 0,
+ "doctype": "Print Format Field Template",
+ "document_type": "Sales Order",
+ "field": "taxes",
+ "idx": 0,
+ "modified": "2021-10-19 18:04:24.443076",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Order Taxes",
+ "owner": "Administrator",
+ "standard": 1,
+ "template": "",
+ "template_file": "templates/print_formats/includes/taxes_and_charges.html"
+}
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index c473395..d61d94c 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -202,7 +202,6 @@
 		{'doctype': "Party Type", "party_type": "Student", "account_type": "Receivable"},
 		{'doctype': "Party Type", "party_type": "Donor", "account_type": "Receivable"},
 
-		{'doctype': "Opportunity Type", "name": "Hub"},
 		{'doctype': "Opportunity Type", "name": _("Sales")},
 		{'doctype': "Opportunity Type", "name": _("Support")},
 		{'doctype': "Opportunity Type", "name": _("Maintenance")},
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index 320cb7b..1412acf 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -1,6 +1,6 @@
 {
  "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",
  "docstatus": 0,
  "doctype": "Workspace",
@@ -10,7 +10,7 @@
  "idx": 0,
  "label": "ERPNext Settings",
  "links": [],
- "modified": "2021-08-05 12:15:59.052328",
+ "modified": "2021-10-26 21:32:55.323591",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "ERPNext Settings",
@@ -28,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/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index db5caf9..4b314a0 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -17,7 +17,6 @@
   "variant_of",
   "item_name",
   "item_group",
-  "is_item_from_hub",
   "stock_uom",
   "column_break0",
   "disabled",
@@ -134,12 +133,7 @@
   "website_specifications",
   "web_long_description",
   "website_content",
-  "total_projected_qty",
-  "hub_publishing_sb",
-  "publish_in_hub",
-  "hub_category_to_publish",
-  "hub_warehouse",
-  "synced_with_hub"
+  "total_projected_qty"
  ],
  "fields": [
   {
@@ -203,14 +197,6 @@
    "search_index": 1
   },
   {
-   "default": "0",
-   "depends_on": "eval:!doc.is_fixed_asset",
-   "fieldname": "is_item_from_hub",
-   "fieldtype": "Check",
-   "label": "Is Item from Hub",
-   "read_only": 1
-  },
-  {
    "fieldname": "stock_uom",
    "fieldtype": "Link",
    "ignore_user_permissions": 1,
@@ -997,41 +983,6 @@
    "read_only": 1
   },
   {
-   "collapsible": 1,
-   "depends_on": "eval:(!doc.is_item_from_hub && !doc.is_fixed_asset)",
-   "fieldname": "hub_publishing_sb",
-   "fieldtype": "Section Break",
-   "label": "Hub Publishing Details"
-  },
-  {
-   "default": "0",
-   "description": "Publish Item to hub.erpnext.com",
-   "fieldname": "publish_in_hub",
-   "fieldtype": "Check",
-   "label": "Publish in Hub"
-  },
-  {
-   "fieldname": "hub_category_to_publish",
-   "fieldtype": "Data",
-   "label": "Hub Category to Publish",
-   "read_only": 1
-  },
-  {
-   "description": "Publish \"In Stock\" or \"Not in Stock\" on Hub based on stock available in this warehouse.",
-   "fieldname": "hub_warehouse",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "Hub Warehouse",
-   "options": "Warehouse"
-  },
-  {
-   "default": "0",
-   "fieldname": "synced_with_hub",
-   "fieldtype": "Check",
-   "label": "Synced With Hub",
-   "read_only": 1
-  },
-  {
    "depends_on": "eval:!doc.__islocal && !doc.is_fixed_asset",
    "fieldname": "over_delivery_receipt_allowance",
    "fieldtype": "Float",
@@ -1078,10 +1029,11 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "max_attachments": 1,
- "modified": "2021-08-26 12:23:07.277077",
+ "modified": "2021-10-27 21:04:00.324786",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 8cc9f74..04e4653 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -123,7 +123,6 @@
 		self.validate_barcode()
 		self.validate_warehouse_for_reorder()
 		self.update_bom_item_desc()
-		self.synced_with_hub = 0
 
 		self.validate_has_variants()
 		self.validate_attributes_in_variants()
diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py
index 6080fb4..6f2a389 100644
--- a/erpnext/stock/doctype/item_alternative/item_alternative.py
+++ b/erpnext/stock/doctype/item_alternative/item_alternative.py
@@ -25,19 +25,29 @@
 			frappe.throw(_("Alternative item must not be same as item code"))
 
 		item_meta = frappe.get_meta("Item")
-		fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no","has_batch_no"]
-		item_data = frappe.db.get_values("Item", self.item_code, fields, as_dict=1)
-		alternative_item_data = frappe.db.get_values("Item", self.alternative_item_code, fields, as_dict=1)
+		fields = ["is_stock_item", "include_item_in_manufacturing","has_serial_no", "has_batch_no", "allow_alternative_item"]
+		item_data = frappe.db.get_value("Item", self.item_code, fields, as_dict=1)
+		alternative_item_data = frappe.db.get_value("Item", self.alternative_item_code, fields, as_dict=1)
 
 		for field in fields:
-			if  item_data[0].get(field) != alternative_item_data[0].get(field):
+			if  item_data.get(field) != alternative_item_data.get(field):
 				raise_exception, alert = [1, False] if field == "is_stock_item" else [0, True]
 
 				frappe.msgprint(_("The value of {0} differs between Items {1} and {2}") \
 					.format(frappe.bold(item_meta.get_label(field)),
 							frappe.bold(self.alternative_item_code),
 							frappe.bold(self.item_code)),
-					alert=alert, raise_exception=raise_exception)
+					alert=alert, raise_exception=raise_exception, indicator="Orange")
+
+		alternate_item_check_msg = _("Allow Alternative Item must be checked on Item {}")
+
+		if not item_data.allow_alternative_item:
+			frappe.throw(alternate_item_check_msg.format(self.item_code))
+		if self.two_way and not alternative_item_data.allow_alternative_item:
+			frappe.throw(alternate_item_check_msg.format(self.item_code))
+
+
+
 
 	def validate_duplicate(self):
 		if frappe.db.get_value("Item Alternative", {'item_code': self.item_code,
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 fd0b368..58b46e1 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -4,6 +4,7 @@
 from __future__ import unicode_literals
 
 import frappe
+from frappe import _dict
 
 test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch']
 
@@ -356,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/get_item_details.py b/erpnext/stock/get_item_details.py
index cbff214..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):
@@ -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/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 e9d5b6a..bdbec52 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -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
 
diff --git a/erpnext/templates/print_formats/includes/taxes_and_charges.html b/erpnext/templates/print_formats/includes/taxes_and_charges.html
new file mode 100644
index 0000000..0d8e383
--- /dev/null
+++ b/erpnext/templates/print_formats/includes/taxes_and_charges.html
@@ -0,0 +1,34 @@
+{% macro render_row(label, value) %}
+<div class="field row">
+	<div class="col-7 {%- if doc.align_labels_right %} text-right{%- endif -%}">
+		<div class="label">{{ label }}</div>
+	</div>
+	<div class="text-right col-5">
+		{{ value }}
+	</div>
+</div>
+{% endmacro %}
+
+{%- macro render_discount_amount(doc) -%}
+	{%- if doc.discount_amount -%}
+	{{ render_row(_(doc.meta.get_label('discount_amount')), '- ' + doc.get_formatted("discount_amount", doc)) }}
+	{%- endif -%}
+{%- endmacro -%}
+
+<div class="row">
+    <div class="col"></div>
+	<div class="col">
+		{%- if doc.apply_discount_on == "Net Total" -%}
+			{{ render_discount_amount(doc) }}
+		{%- endif -%}
+		{%- for charge in doc.taxes -%}
+			{%- if (charge.tax_amount or print_settings.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
+			{{ render_row(charge.get_formatted("description"), charge.get_formatted('tax_amount', doc)) }}
+			{%- endif -%}
+		{%- endfor -%}
+		{%- if doc.apply_discount_on == "Grand Total" -%}
+			{{ render_discount_amount(doc) }}
+		{%- endif -%}
+	</div>
+</div>
+
diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py
index 881f286..3ce68d8 100644
--- a/erpnext/tests/test_woocommerce.py
+++ b/erpnext/tests/test_woocommerce.py
@@ -12,12 +12,6 @@
 
 class TestWoocommerce(unittest.TestCase):
 	def setUp(self):
-		if not frappe.db.exists('Company', 'Woocommerce'):
-			company = frappe.new_doc("Company")
-			company.company_name = "Woocommerce"
-			company.abbr = "W"
-			company.default_currency = "INR"
-			company.save()
 
 		woo_settings = frappe.get_doc("Woocommerce Settings")
 		if not woo_settings.secret:
@@ -26,14 +20,14 @@
 			woo_settings.api_consumer_key = "ck_fd43ff5756a6abafd95fadb6677100ce95a758a1"
 			woo_settings.api_consumer_secret = "cs_94360a1ad7bef7fa420a40cf284f7b3e0788454e"
 			woo_settings.enable_sync = 1
-			woo_settings.company = "Woocommerce"
-			woo_settings.tax_account = "Sales Expenses - W"
-			woo_settings.f_n_f_account = "Expenses - W"
+			woo_settings.company = "_Test Company"
+			woo_settings.tax_account = "Sales Expenses - _TC"
+			woo_settings.f_n_f_account = "Expenses - _TC"
 			woo_settings.creation_user = "Administrator"
 			woo_settings.save(ignore_permissions=True)
 
 	def test_sales_order_for_woocommerce(self):
-		frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel &times; 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]}
+		frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"_Test Company","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel &times; 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]}
 		order()
 
 		self.assertTrue(frappe.get_value("Customer",{"woocommerce_email":"tony@gmail.com"}))